Why you should use {providedIn: “root”}
not always, not by default, but only when you need it and know how to use it.
Why do we need the Injectable()
decorator?
To let the Angular Dependency Injection mechanism inject it. So by definition, if you decorate your class with the Injectable()
, you want to later use it in your components (or other services), and let Angular DI take care of the dependencies of this decorated class.
There is one configurable option: providedIn
, which currently has only 2 meaningful values: root
and undefined
(default).
Services with providedIn: root
will be instantiated just once. Without this option — will be instantiated every time, when a component that has them in its providers
array is instantiated. That is an important difference.
Why ‘use with care’?
Services, providedIn: root
will be shared across your application. It means, that every component, every service that injects such services, will get the same instance. It is not “bad”, or “wrong”, it just means that you should write the code in such services with constant consideration, that multiple components will use it at the same time.
Sometimes it is exactly what you need! For example, HttpClient
with its interceptors. You want to configure them once and use the same instance of HttpClient
across the whole application.
But quite often developers just add it because tutorials told them to, and they don’t know other ways of injecting a service. And not every time developers are actually aware of the fact that multiple components will share the same instance of the service they create.
Here is a modified example from Angular documentation:
import {Injectable} from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class CalculatorService {
scale = 1;
add(x: number, y: number) {
return (x + y) * this.scale;
}
}
If some component modifies the scale field, all other components that use this service will be affected. It is a simplified example, in the real code things are more sophisticated.
How to use it correctly?
The main rule of globally shared services: they should either not have an internal state, or their state should not be modifiable after instantiation, or their code should be aware that multiple different consumers will modify this state in a chaotic order (that’s why methods that modify the state should be atomic — only one call should be expected from a consumer, not a set of calls, executed in pre-defined order).
I’m not saying “do not use it”, but: if you want to share the single instance of that service with all the components that use it — do it, and write the code accordingly.
If you want a new instance of this class whenever some component is instantiated, and have it destroyed when a component is destroyed — do not add providedIn: root
, just add Injectable()
and add this service to the providers
array of that component.
If your class has no dependencies and you don’t need it to be shared across multiple components — then you don’t even need the Injectable()
decorator, you can just use new.
In my article Advanced Usage of Angular Dependency Injection you can find more examples of using Angular DI in your apps.
🪽 Do you like this article? Share it and let it fly! 🛸
💙 If you enjoy my articles, consider following me on Twitter, and/or subscribing to receive my new articles by email.
Top comments (0)