Services and Dependency Injection
Angular has a bit of similarity to the .NET ecosystem in regards to handling services. The classes that provide some specific functionalities (like logging) can be brought into other classes (e.g. components) using Dependency Injection. Here’s a simple example:
The highlighted lines show, in order:
- How to create a scope for
LoggerService
- Constructor injection
- Using the injected service.
Services in Angular are just typical TypeScript classes. They do not use any Angular-specific decorator (unless you want to inject it into some other service).
Scope
The way how we injected the service into the component in the example above has some consequences:
- the child components (direct and indirect) will receive the same instance of
the
LoggerService
(if they ask for it) - all the other components (other than the children of
MyComponent
) would receive different instance(s) ofLoggerService
The Angular injector is hierarchical. We can inject services into:
- components -
providers
of a module or component - services -
providers
of a module and@Injectable
- directives
- pipes
Singleton
Providing a service via SomeComponent
makes it a singleton among the children
tree of that component.
Providing a service via the AppComponent
makes it a singleton among all
components (since AppComponent
is usually the root of any Angular app).
Providing a service in ANY eager-loaded module
(e.g., AppModule
), makes it a global singleton (for both all the components
and services). T
Providing a service in a lazy-loaded module, makes that service available in entities declared within that module only. If some service is provided both globally and in lazy-loaded module, the lazy-loaded module (and its entities) will get its own instance of that service.
The way how Angular sets up DI is different from the .NET’s way of doing that. In .NET we decide whether the service should be a singleton or not at the application root level. In Angular, each class/component may decide whether it wants to reuse some service instance or to get a new one.
Injecting into Services
Services may be injected into other services. Services do not use decorators by
default, so there is no providers
array. Instead, services that want to have
some dependency injected, need to use the special @Injectable
decorator.
Here’s an example:
Angular will try to match all the arguments that are required by the
constructor with DI. In this case, Logger
would be injected.
Provide Strategies
Until now, in the module’s providers
array, we’d just list the names of the
services. By default, the class’s name acts as a token that represents that
class. Angular’s injector is more flexible and we can define a token and
dependency to be different things. It’s a bit similar to how in .NET there’s an
interface that classes depend on and a class that actually implements that
interface and gets injected.
Providers can be also defined like this:
The factory injection function can be defined in a separate file, often called
<service>.service.provider.ts
.
Tokens
The injection tokens are recommended to be created like this: