Building Angular components, most developers will have already used this to inject features like the HTTP client or the FormBuilder to be used in a component. A common service example might look like this:
Angular’s own features can be injected via their class identity (e.g.
HttpClient). You can also inject any components, services or pipes that are registered within a module.
Angular also provides a mechanism for custom injections, this means items like configurations or instances of external libraries can be passed into any constructor within your module or any item within the dependency tree. To do this the library needs to provide an
InjectionToken which represents the key in the dependency tree, and use the
Inject decorator to provide an instance into a class constructor (this is covered later in this post).
Creating The Service
I recently worked on refactoring some code where in a component it had some code to handle getting a session object from
sessionStorage. When working with Angular’s
ng serve making changes will often result in a refresh, destroying any existing session. This feature allowed session storage to be turned on and keep it between refreshes by allowing the component to check if a value existed in
Using the principles in SOLID I moved the code into a service. Having a service do one thing it made the code easier to understand. The unit test for this is also smaller and can cover most of the component.
The service has one additional property which is
storagePrefix, used to make each instance of the service unique to it’s component.
In it’s current implementation it’s usable, but it has a few issues. The main one is that it uses
sessionStorage but trusts that it exists on the global object, this means we should always expect it to be available, and is a bad practice. It also limits this service to only use
sessionStorage and no other key/value store. It would be better if we could provide it to our service instead.
We can also set the storagePrefix via our app configuration and means our component doesn’t need to understand this configuration (yet!) so it avoids us having to call the
init method within a component’s
By making these injectable, we will not only be able to provide our
sessionStorage but we can support any module or
library that conforms to the Web Storage API
. This means we no longer need to rely on the global object and we have more control over what we inject.
The powerful feature of dependency injection is the ability to pass instances of other non-angular applications. For example you could pass the instance of a
jQuery application and call it’s methods from your application (you’ll also want to check out NgZone
when running these, but I’ll discuss that in a future post).
This allows for the transition of applications over time within an organisation, rather than having to replace all applications in one go. It’s a good way to safely inject features from third-party libraries and means passing mock versions is easier for tests.
Creating an injectable service and module
Making features injectable
First we need to define the interface of the object want to use for the configuration. This interface provides a contract in the
forRoot method described below and tells developers what options they can provide to the application
Our configuration will require a
storagePrefix as a string - this is need to set the name of the store. We also pass our
Storage instance, but with this we can set a default value in our component
Next we will create an
InjectionToken, a token that is usually constant representation of something we want to provide via dependency injection. You can use any type of object as your token type, and can even provide a factory method to provide dependencies. Check out the Tree-shakeable Injection Token
to see how you can do some advanced use. In our case we will just have the component config object.
Now we have the token service we will provide a simple configuration with this token which will be an object, but a token can be used to inject anything from a string to a function, or even a class instance.
Making the Angular module configurable
To make the service configurable, it should be provided via a NgModule - a regular module might look something like this:
To make this module accept a configuration we need to add a method that can return an instance of our module with custom providers. Providers are an important part of Angular’s dependency injection as these are the instance of what child components can inject - all will hopefully become clear through the code example.
First we will add a static
forRoot method to the class. This method provides a factory function where we can pass a configuration in the application imports.
The factory returns an instance of that module with the provided configuration, and this passed into the items with
Inject . The module can then be further used without the
forRoot as long as it’s configured at the root of the application (see SkipSelf
Before we can use this in our application we also need to update the
ComponentService itself to use this configuration. Here the Inject
decorator will be used to tell the constructor that the
ComponentServiceConfig this service needs will be available to inject via the
As the config is set with
private in the constructor, TypeScript knows to set this as a property on the class. This is the same as writing the following code and provides a clean shorthand to set up properties.
Providing the features in the app
Now that we have our configurable module, the Angular application can be provided with a configuration when importing.
As the module now accepts any
Storage type, we’ll instead provide
localStorage for this application, along side the
Another example is we could have an NPM library that provides the same interface, such as localStorage-ponyfill
, it could be used in your application - or you may want to provide it to a
With this technique, any kind of primitive or complex object, method or class can now be passed into your feature modules - and is a useful way to provide third-party libraries to your modules, configuring not just your services, but also components, pipes and stores.
Any class with a
constructor (provided it’s in the same dependency tree) can use
Inject with your exported token to access this configuration.
Hopefully you will also find this technique useful to improve the configurable and testable features of your module. Feel free to respond on twitter if you spot any typos or errors.