Welcome to Part 4 of this series reviewing the Pluralsight course Aurelia Fundamentals by Brian Noyes.
Brian is CTO and Architect at Solliance, an expert technology solutions development company.
Brian is a Microsoft Regional Director and MVP, and specializes in rich client technologies including XAML and HTML 5, as well as building the services that back them with WCF and ASP.NET Web API.
You can follow Brian through his blog at http://briannoyes.net
Also in this series:
Using Dependency Injection in Aurelia
Purpose of Dependency Injection and Related Patterns
Dependency Injection is mostly about having loose coupling between dependent components.
Brian also mentions a couple of closely related patterns: Inversion of Control and Service Location
There are also many other Pluralsight Courses which cover Dependency Injection and/or Inversion of Control.
The ones that I have seen and found worthwhile are:
C# Interfaces by Jeremy Clark
RequireJS Dependency Injection and Module Loading by Jeff Valore
Understanding ASP.NET Core by Roland Guijt
SOLID Principles of Object Oriented Design by Steve Smith
Inversion of Control by John Sonmez
Practical IoC with ASP.NET MVC4 by John Sonmez
To learn more about the Service Locator pattern, see John Brown’s Service Locator module in the Design Patterns Library course. For just a very brief Service Locator summary read this wikipedia article.
Also see my Design Patterns page.
Inversion of Control/Dependency Injection: Problem and Solution
Brian gives an example:
- Some Consumer object is dependent on A
- A is dependent on B and C
- B is dependent on D
- C is dependent on D and E
The brute force way to implement this is to new up every object before it is used, i.e. in A we would have new B(); and new C(); and so on. There are two problems with this:
- Very tight coupling between each component
- There may be shared state, and we can’t use that when we are creating separate instances of that state
There are some possible hacky workarounds, but Dependency Injection and Inversion of Control offers us a much better solution.
Brian explains the process that an IoC container goes through in this example.
Using ‘inject’ Decorator in Aurelia
The pattern is:
- import the necessary modules
- use the @inject decorator, passing in a service as a parameter
Brian says here that they are an ES2016 feature. This is not true because they are currently a just a ES2017 stage 1 proposal. It is supported by TypeScript, and you can read more about that in the TypeScript Handbook.
Brian explains that there is an alternate solution supported by Aurelia which is static property injection, with an array of type names
static inject = [SomeService];
Dependency Injection in Action
This demonstration uses the class decorator syntax to inject DataCache into the Event and Events class constructors.
We see that the DataCache is shared state, and that the default mode of dependency injection in Aurelia is a singleton.
Declaratively Registering Lifetime Instances in Aurelia
We can indicate on class what instance lifetime it should have in container. There are two main patterns:
- Singleton – constructed on first injection, same reference passed into other injections
- Transient – new instance created for each injection
As the default mode is singleton, it is not necessary to specify the instance lifetime as a singleton, but you can make it explicit if you prefer.
Using Lifetime Management Decorators in Aurelia
Brian demonstrates how to use the transient decorator to specify a transient property.
We see in the console logs 3 separate constructions of DataCache
Explicitly Registering Types and Instances in Aurelia
There are three options:
- instance – just new up an object, then register it with the container
For further information see Object Lifetime, Child Containers and Default Behavior
Using Framework Configuration to Explicitly Register Types
This demo updates our configure function again, and we see how to implement each of the three options described in the previous lesson.
Using Resolvers in Aurelia
There are currently 4 out of the box resolvers. Three of them are described in this lesson
- Lazy.of(T) – Injects a function for lazily evaluating the dependency
- All.of(T) – Injects an array of all services registered with the provided key
- Optional.of(T) – Injects an instance of a class only if it already exists in the container; null otherwise.
See Resolvers in the official documentation to learn Parent.of(T)
Lazy Loading and Plugin Instancing with Aurelia
First a demo of using Lazy.of(T).
Brian creates an ImLazy class with a constructor and a doStuff method.
Then events.js is created with Events class and the method createAndUseImLazy.
In events.html there’s a button that calls createAndUseImLazy
The effect of all this is to defer the construction of the dependent object until the point where we need to use it.
Next, a demo of All.of(T), which is useful for plugins. We might want to register all plugins that are associated with a 3rd party system.
First we need a plugin definition. We see a class PlugIn1 with a doPlugInStuff method and another class PlugIn2 with another doPlugInStuff method.
In main.js we see how to import these plugin types and register them as transient.
Finally we inject All.of(“SuperPlugIn”) into our Events class.
So we have got multiple instances of different types all registered under the same name “SuperPlugIn”.
Registering Global Dependencies
This is a great way to avoid repetitive import declarations. We register a dependency globally through the app configure method.
It will then be available for injection into our modules without an import statement, and can be used in our views without a <require> element.
We do this with the globalResources keyword.
For more information see the FrameworkConfiguration documentation.