Angular Dependency Injection Understood III
Jordi Riera
Posted on November 14, 2022
In the last chapter we learned how angular determines who is responsible to provide our dependencies. Today we will explore how once the provider has been determined we can choose what and how we provide our dependencies.
By default when we define a provider token we use the syntax:
providers:[Service]
This in fact is a shorthand version for the following:
providers:[{provide: Service, useClass: Service}]
So what is this provider configuration object? And what properties does it take?
Angular gives us the option to define how we provide our Service.
There are 4 provider definition objects that tell the provider how to create our Service.
Let's review them one by one and when to use them.
We'll begin with the default one:
useClass
With this definition we can choose a specific class to be used as the base to instantiate our service. Taking advantage of the different scopes of the injectors we can use different classes to instantiate the service depending on where we are providing the dependency.
for instance we could do:
In what cases would this be useful?
Let's imagine we are creating a generic MatStepper and we want a different action on the click of the final step depending on which component or module is using that generic stepper. We can provide a StepperService on the component but use a different class to actually implement it so the actions would be different even if we are calling the same service and same method
Another useful case is during testing, sometimes we want a simpler implementation of our service while doing unit tests. In our testbed we can provide the same dependency using a simpler class that satisfies our needs in that test
useExisting
This is somewhat the opposite of useClass. Instead of mapping one token to different classes we are mapping different tokens to the same class. In some way is like using different aliases for the same class
providers:[NewLogger,{provide:OldLogger, useExisting:NewLogger}]
In this example wherever we are injecting the OldLogger we will be using the already existing instance of NewLogger.
This approach can be helpful if we want to replace a legacy service with a new one, whenever replacing the implementations of the legacy service can be too costly.
If in this case we used useClass, we would create two different instances of the same NewLogger
useFactory
There might be some cases where we can't know before hand which implementation of our service should be instanced, because it might be dependent on runtime conditions.
For these cases angular gives us the option to pass a function that will create the service during runtime using parameters known at that moment by the application. This function is called a service factory (hence useFactory)
A typical use case might be that we want to instantiate a service via one class or another depending of user permissions.
It is important to add the deps property when the factory function has other dependencies itself, in this case the LoggerServiceFactory depends on the UserService, so we added it in the deps in its provider object
useValue
When we want to provide constants or configuration parameters in the DI we can use useValue to assign static values to our providers
We'll get more in detail with useValue in the next chapter when we explore what injectionTokens are.
For now we have seen 3 very useful modifiers for our providers and common cases where they might be applicable.
As usual any questions you may have I'll be glad to answer and feel free to follow me to get more updates on my articles.
Thanks!
Posted on November 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
February 1, 2024