2019-12-02 15:47:19 -05:00
# Dependency injection in action
2017-02-22 13:09:39 -05:00
2020-10-30 10:41:33 -04:00
This guide explores many of the features of dependency injection (DI) in Angular.
2017-03-27 11:08:53 -04:00
2018-07-19 18:00:08 -04:00
< div class = "alert is-helpful" >
2017-03-27 11:08:53 -04:00
2020-10-30 10:41:33 -04:00
See the < live-example > < / live-example > for a working example containing the code snippets in this guide.
2017-02-22 13:09:39 -05:00
2017-04-10 11:51:13 -04:00
< / div >
2017-03-27 11:08:53 -04:00
2017-03-31 19:57:13 -04:00
{@a multiple-service-instances}
2017-02-22 13:09:39 -05:00
## Multiple service instances (sandboxing)
2018-05-31 15:02:50 -04:00
Sometimes you want multiple instances of a service at *the same level* of the component hierarchy.
2017-02-22 13:09:39 -05:00
2017-03-31 07:23:16 -04:00
A good example is a service that holds state for its companion component instance.
2017-03-31 19:57:13 -04:00
You need a separate instance of the service for each component.
2017-02-22 13:09:39 -05:00
Each service has its own work-state, isolated from the service-and-state of a different component.
2017-03-31 19:57:13 -04:00
This is called *sandboxing* because each service and component instance has its own sandbox to play in.
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
{@a hero-bios-component}
2018-05-31 15:02:50 -04:00
In this example, `HeroBiosComponent` presents three instances of `HeroBioComponent` .
2017-03-30 15:04:18 -04:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-bios.component.ts" region = "simple" header = "ap/hero-bios.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
2017-03-31 07:23:16 -04:00
Each `HeroBioComponent` can edit a single hero's biography.
2018-05-31 15:02:50 -04:00
`HeroBioComponent` relies on `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero.
2017-02-22 13:09:39 -05:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-cache.service.ts" region = "service" header = "src/app/hero-cache.service.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
Three instances of `HeroBioComponent` can't share the same instance of `HeroCacheService` ,
as they'd be competing with each other to determine which hero to cache.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
Instead, each `HeroBioComponent` gets its *own* `HeroCacheService` instance
by listing `HeroCacheService` in its metadata `providers` array.
2017-02-22 13:09:39 -05:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-bio.component.ts" region = "component" header = "src/app/hero-bio.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
The parent `HeroBiosComponent` binds a value to `heroId` .
`ngOnInit` passes that ID to the service, which fetches and caches the hero.
2017-02-22 13:09:39 -05:00
The getter for the `hero` property pulls the cached hero from the service.
2018-05-31 15:02:50 -04:00
The template displays this data-bound property.
2017-02-22 13:09:39 -05:00
2017-04-21 20:21:45 -04:00
Find this example in < live-example name = "dependency-injection-in-action" > live code< / live-example >
2017-03-31 07:23:16 -04:00
and confirm that the three `HeroBioComponent` instances have their own cached hero data.
2017-03-30 15:04:18 -04:00
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/hero-bios.png" alt = "Bios" >
< / div >
2017-02-22 13:09:39 -05:00
{@a qualify-dependency-lookup}
2018-05-31 15:02:50 -04:00
## Qualify dependency lookup with parameter decorators
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
When a class requires a dependency, that dependency is added to the constructor as a parameter.
When Angular needs to instantiate the class, it calls upon the DI framework to supply the dependency.
By default, the DI framework searches for a provider in the injector hierarchy,
2018-07-23 20:11:30 -04:00
starting at the component's local injector of the component, and if necessary bubbling up
2018-05-31 15:02:50 -04:00
through the injector tree until it reaches the root injector.
2017-03-31 19:57:13 -04:00
2018-07-23 20:11:30 -04:00
* The first injector configured with a provider supplies the dependency (a service instance or value) to the constructor.
2017-02-22 13:09:39 -05:00
2019-02-15 05:04:05 -05:00
* If no provider is found in the root injector, the DI framework throws an error.
2017-03-31 07:23:16 -04:00
2018-05-31 15:02:50 -04:00
There are a number of options for modifying the default search behavior, using _parameter decorators_
2018-07-23 20:11:30 -04:00
on the service-valued parameters of a class constructor.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
{@a optional}
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
### Make a dependency `@Optional` and limit search with `@Host`
2017-02-22 13:09:39 -05:00
2018-07-23 20:11:30 -04:00
Dependencies can be registered at any level in the component hierarchy.
When a component requests a dependency, Angular starts with that component's injector
and walks up the injector tree until it finds the first suitable provider.
2018-05-31 15:02:50 -04:00
Angular throws an error if it can't find the dependency during that walk.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
In some cases, you need to limit the search or accommodate a missing dependency.
You can modify Angular's search behavior with the `@Host` and `@Optional` qualifying
2018-07-23 20:11:30 -04:00
decorators on a service-valued parameter of the component's constructor.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
* The `@Optional` property decorator tells Angular to return null when it can't find the dependency.
2017-03-31 19:57:13 -04:00
2018-07-23 20:11:30 -04:00
* The `@Host` property decorator stops the upward search at the *host component* .
The host component is typically the component requesting the dependency.
However, when this component is projected into a *parent* component,
2018-05-31 15:02:50 -04:00
that parent component becomes the host. The following example covers this second case.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
These decorators can be used individually or together, as shown in the example.
This `HeroBiosAndContactsComponent` is a revision of `HeroBiosComponent` which you looked at [above ](guide/dependency-injection-in-action#hero-bios-component ).
2017-02-22 13:09:39 -05:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-bios.component.ts" region = "hero-bios-and-contacts" header = "src/app/hero-bios.component.ts (HeroBiosAndContactsComponent)" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
Focus on the template:
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-bios.component.ts" region = "template" header = "dependency-injection-in-action/src/app/hero-bios.component.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
Now there's a new `<hero-contact>` element between the `<hero-bio>` tags.
2017-03-31 19:57:13 -04:00
Angular *projects* , or *transcludes* , the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
2018-05-31 15:02:50 -04:00
placing it in the `<ng-content>` slot of the `HeroBioComponent` template.
2017-02-22 13:09:39 -05:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-bio.component.ts" region = "template" header = "src/app/hero-bio.component.ts (template)" > < / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The result is shown below, with the hero's telephone number from `HeroContactComponent` projected above the hero description.
2017-03-30 15:04:18 -04:00
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/hero-bio-and-content.png" alt = "bio and contact" >
< / div >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
Here's `HeroContactComponent` , which demonstrates the qualifying decorators.
2017-03-31 19:57:13 -04:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-contact.component.ts" region = "component" header = "src/app/hero-contact.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
Focus on the constructor parameters.
2017-03-31 19:57:13 -04:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-contact.component.ts" region = "ctor-params" header = "src/app/hero-contact.component.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The `@Host()` function decorating the `heroCache` constructor property ensures that
2017-03-31 19:57:13 -04:00
you get a reference to the cache service from the parent `HeroBioComponent` .
Angular throws an error if the parent lacks that service, even if a component higher
2018-05-31 15:02:50 -04:00
in the component tree includes it.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
A second `@Host()` function decorates the `loggerService` constructor property.
2017-03-31 19:57:13 -04:00
The only `LoggerService` instance in the app is provided at the `AppComponent` level.
2017-02-22 13:09:39 -05:00
The host `HeroBioComponent` doesn't have its own `LoggerService` provider.
2018-05-31 15:02:50 -04:00
Angular throws an error if you haven't also decorated the property with `@Optional()` .
When the property is marked as optional, Angular sets `loggerService` to null and the rest of the component adapts.
2017-02-22 13:09:39 -05:00
2017-03-27 11:08:53 -04:00
2018-05-31 15:02:50 -04:00
Here's `HeroBiosAndContactsComponent` in action.
2017-03-30 15:04:18 -04:00
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/hero-bios-and-contacts.png" alt = "Bios with contact into" >
< / div >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
If you comment out the `@Host()` decorator, Angular walks up the injector ancestor tree
2018-07-23 20:11:30 -04:00
until it finds the logger at the `AppComponent` level.
The logger logic kicks in and the hero display updates
2018-05-31 15:02:50 -04:00
with the "!!!" marker to indicate that the logger was found.
2017-03-30 15:04:18 -04:00
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/hero-bio-contact-no-host.png" alt = "Without @Host " >
< / div >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
If you restore the `@Host()` decorator and comment out `@Optional` ,
the app throws an exception when it cannot find the required logger at the host component level.
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
`EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)`
2018-05-31 15:02:50 -04:00
### Supply a custom provider with `@Inject`
2018-07-23 20:11:30 -04:00
Using a custom provider allows you to provide a concrete implementation for implicit dependencies, such as built-in browser APIs. The following example uses an `InjectionToken` to provide the [localStorage ](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage ) browser API as a dependency in the `BrowserStorageService` .
2018-05-31 15:02:50 -04:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/storage.service.ts" header = "src/app/storage.service.ts" >
2018-05-31 15:02:50 -04:00
< / code-example >
2020-01-19 00:44:53 -05:00
The `factory` function returns the `localStorage` property that is attached to the browser window object. The `Inject` decorator is a constructor parameter used to specify a custom provider of a dependency. This custom provider can now be overridden during testing with a mock API of `localStorage` instead of interacting with real browser APIs.
2018-05-31 15:02:50 -04:00
2018-07-23 20:11:30 -04:00
{@a skip}
2018-05-31 15:02:50 -04:00
### Modify the provider search with `@Self` and `@SkipSelf`
Providers can also be scoped by injector through constructor parameter decorators. The following example overrides the `BROWSER_STORAGE` token in the `Component` class `providers` with the `sessionStorage` browser API. The same `BrowserStorageService` is injected twice in the constructor, decorated with `@Self` and `@SkipSelf` to define which injector handles the provider dependency.
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/storage.component.ts" header = "src/app/storage.component.ts" >
2018-05-31 15:02:50 -04:00
< / code-example >
2019-10-14 16:59:00 -04:00
Using the `@Self` decorator, the injector only looks at the component's injector for its providers. The `@SkipSelf` decorator allows you to skip the local injector and look up in the hierarchy to find a provider that satisfies this dependency. The `sessionStorageService` instance interacts with the `BrowserStorageService` using the `sessionStorage` browser API, while the `localStorageService` skips the local injector and uses the root `BrowserStorageService` that uses the `localStorage` browser API.
2018-05-31 15:02:50 -04:00
2017-03-31 19:57:13 -04:00
{@a component-element}
## Inject the component's DOM element
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
Although developers strive to avoid it, many visual effects and third-party tools, such as jQuery,
2017-03-31 07:23:16 -04:00
require DOM access.
2018-05-31 15:02:50 -04:00
As a result, you might need to access a component's DOM element.
2017-02-22 13:09:39 -05:00
2020-10-30 10:41:33 -04:00
To illustrate, here's a minimal version of `HighlightDirective` from
2017-03-31 19:57:13 -04:00
the [Attribute Directives ](guide/attribute-directives ) page.
2017-02-22 13:09:39 -05:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/highlight.directive.ts" header = "src/app/highlight.directive.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
The directive sets the background to a highlight color when the user mouses over the
2018-05-31 15:02:50 -04:00
DOM element to which the directive is applied.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
Angular sets the constructor's `el` parameter to the injected `ElementRef` .
2018-07-23 20:11:30 -04:00
(An `ElementRef` is a wrapper around a DOM element,
2018-05-31 15:02:50 -04:00
whose `nativeElement` property exposes the DOM element for the directive to manipulate.)
2017-02-22 13:09:39 -05:00
2017-03-31 07:23:16 -04:00
The sample code applies the directive's `myHighlight` attribute to two `<div>` tags,
2017-02-22 13:09:39 -05:00
first without a value (yielding the default color) and then with an assigned color value.
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/app.component.html" region = "highlight" header = "src/app/app.component.html (highlight)" > < / code-example >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
2017-03-30 15:04:18 -04:00
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/highlight.png" alt = "Highlighted bios" >
< / div >
2017-03-31 19:57:13 -04:00
{@a defining-providers}
2017-02-22 13:09:39 -05:00
### Defining providers
2018-05-31 15:02:50 -04:00
A dependency can't always be created by the default method of instantiating a class.
2018-07-23 20:11:30 -04:00
You learned about some other methods in [Dependency Providers ](guide/dependency-injection-providers ).
2018-05-31 15:02:50 -04:00
The following `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them.
2017-04-26 08:11:02 -04:00
It's visually simple: a few properties and the logs produced by a logger.
2017-03-30 15:04:18 -04:00
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/hero-of-month.png" alt = "Hero of the month" >
< / div >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The code behind it customizes how and where the DI framework provides dependencies.
2018-07-23 20:11:30 -04:00
The use cases illustrate different ways to use the [*provide* object literal ](guide/dependency-injection-providers#provide ) to associate a definition object with a DI token.
2017-03-31 19:57:13 -04:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "hero-of-the-month" header = "hero-of-the-month.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The `providers` array shows how you might use the different provider-definition keys;
`useValue` , `useClass` , `useExisting` , or `useFactory` .
2017-02-22 13:09:39 -05:00
{@a usevalue}
2018-05-31 15:02:50 -04:00
#### Value providers: `useValue`
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
The `useValue` key lets you associate a fixed value with a DI token.
2017-03-31 19:57:13 -04:00
Use this technique to provide *runtime configuration constants* such as website base addresses and feature flags.
2018-05-31 15:02:50 -04:00
You can also use a value provider in a unit test to provide mock data in place of a production data service.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The `HeroOfTheMonthComponent` example has two value providers.
2017-02-22 13:09:39 -05:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "use-value" header = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
* The first provides an existing instance of the `Hero` class to use for the `Hero` token, rather than
2018-07-23 20:11:30 -04:00
requiring the injector to create a new instance with `new` or use its own cached instance.
2018-05-31 15:02:50 -04:00
Here, the token is the class itself.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
* The second specifies a literal string resource to use for the `TITLE` token.
The `TITLE` provider token is *not* a class, but is instead a
special kind of provider lookup key called an [injection token ](guide/dependency-injection-in-action#injection-token ), represented by
2018-07-23 20:11:30 -04:00
an `InjectionToken` instance.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
You can use an injection token for any kind of provider but it's particularly
2017-03-31 19:57:13 -04:00
helpful when the dependency is a simple value like a string, a number, or a function.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The value of a *value provider* must be defined before you specify it here.
The title string literal is immediately available.
The `someHero` variable in this example was set earlier in the file as shown below.
You can't use a variable whose value will be defined later.
2017-02-22 13:09:39 -05:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "some-hero" header = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
Other types of providers can create their values *lazily* ; that is, when they're needed for injection.
2017-02-22 13:09:39 -05:00
{@a useclass}
2017-03-31 19:57:13 -04:00
2018-07-23 20:11:30 -04:00
#### Class providers: `useClass`
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The `useClass` provider key lets you create and return a new instance of the specified class.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
You can use this type of provider to substitute an *alternative implementation*
2018-07-23 20:11:30 -04:00
for a common or default class.
2018-05-31 15:02:50 -04:00
The alternative implementation could, for example, implement a different strategy,
extend the default class, or emulate the behavior of the real class in a test case.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The following code shows two examples in `HeroOfTheMonthComponent` .
2017-02-22 13:09:39 -05:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "use-class" header = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
The first provider is the *de-sugared* , expanded form of the most typical case in which the
2017-03-31 19:57:13 -04:00
class to be created (`HeroService`) is also the provider's dependency injection token.
2018-05-31 15:02:50 -04:00
The short form is generally preferred; this long form makes the details explicit.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The second provider substitutes `DateLoggerService` for `LoggerService` .
`LoggerService` is already registered at the `AppComponent` level.
When this child component requests `LoggerService` , it receives a `DateLoggerService` instance instead.
2017-03-27 11:08:53 -04:00
2018-07-19 18:00:08 -04:00
< div class = "alert is-helpful" >
2017-03-27 11:08:53 -04:00
2018-05-31 15:02:50 -04:00
This component and its tree of child components receive `DateLoggerService` instance.
2017-03-27 11:08:53 -04:00
Components outside the tree continue to receive the original `LoggerService` instance.
2017-04-10 11:51:13 -04:00
< / div >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
`DateLoggerService` inherits from `LoggerService` ; it appends the current date/time to each message:
2017-03-30 15:04:18 -04:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/date-logger.service.ts" region = "date-logger-service" header = "src/app/date-logger.service.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
{@a useexisting}
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
#### Alias providers: `useExisting`
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
The `useExisting` provider key lets you map one token to another.
In effect, the first token is an *alias* for the service associated with the second token,
creating two ways to access the same service object.
2017-02-22 13:09:39 -05:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "use-existing" header = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
You can use this technique to narrow an API through an aliasing interface.
The following example shows an alias introduced for that purpose.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
Imagine that `LoggerService` had a large API, much larger than the actual three methods and a property.
2017-03-31 19:57:13 -04:00
You might want to shrink that API surface to just the members you actually need.
2018-05-31 15:02:50 -04:00
In this example, the `MinimalLogger` [class-interface ](#class-interface ) reduces the API to two members:
2017-02-22 13:09:39 -05:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/minimal-logger.service.ts" header = "src/app/minimal-logger.service.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The following example puts `MinimalLogger` to use in a simplified version of `HeroOfTheMonthComponent` .
2017-03-31 19:57:13 -04:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts" header = "src/app/hero-of-the-month.component.ts (minimal version)" > < / code-example >
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger` , so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor.
2017-03-30 15:04:18 -04:00
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/minimal-logger-intellisense.png" alt = "MinimalLogger restricted API" >
< / div >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
Behind the scenes, Angular sets the `logger` parameter to the full service registered under the `LoggingService` token, which happens to be the `DateLoggerService` instance that was [provided above ](guide/dependency-injection-in-action#useclass ).
2017-03-31 19:57:13 -04:00
2018-07-19 18:00:08 -04:00
< div class = "alert is-helpful" >
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
This is illustrated in the following image, which displays the logging date.
2017-03-30 15:04:18 -04:00
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/date-logger-entry.png" alt = "DateLoggerService entry" >
< / div >
2017-02-22 13:09:39 -05:00
2017-04-10 11:51:13 -04:00
< / div >
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
{@a usefactory}
2017-03-31 19:57:13 -04:00
2018-07-23 20:11:30 -04:00
#### Factory providers: `useFactory`
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
The `useFactory` provider key lets you create a dependency object by calling a factory function,
as in the following example.
2017-02-22 13:09:39 -05:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "use-factory" header = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The injector provides the dependency value by invoking a factory function,
that you provide as the value of the `useFactory` key.
Notice that this form of provider has a third key, `deps` , which specifies
dependencies for the `useFactory` function.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
Use this technique to create a dependency object with a factory function
whose inputs are a combination of *injected services* and *local state* .
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
The dependency object (returned by the factory function) is typically a class instance,
but can be other things as well.
In this example, the dependency object is a string of the names of the runners up
2017-02-22 13:09:39 -05:00
to the "Hero of the Month" contest.
2018-05-31 15:02:50 -04:00
In the example, the local state is the number `2` , the number of runners up that the component should show.
The state value is passed as an argument to `runnersUpFactory()` .
The `runnersUpFactory()` returns the *provider factory function* , which can use both
the passed-in state value and the injected services `Hero` and `HeroService` .
2017-02-22 13:09:39 -05:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/runners-up.ts" region = "factory-synopsis" header = "runners-up.ts (excerpt)" > < / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The provider factory function (returned by `runnersUpFactory()` ) returns the actual dependency object,
the string of names.
2017-03-31 19:57:13 -04:00
2018-07-23 20:11:30 -04:00
* The function takes a winning `Hero` and a `HeroService` as arguments.
2017-03-31 07:23:16 -04:00
Angular supplies these arguments from injected values identified by
the two *tokens* in the `deps` array.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
* The function returns the string of names, which Angular than injects into
the `runnersUp` parameter of `HeroOfTheMonthComponent` .
2017-03-27 11:08:53 -04:00
2018-07-19 18:00:08 -04:00
< div class = "alert is-helpful" >
2017-03-27 11:08:53 -04:00
2017-03-31 07:23:16 -04:00
The function retrieves candidate heroes from the `HeroService` ,
2017-02-22 13:09:39 -05:00
takes `2` of them to be the runners-up, and returns their concatenated names.
2017-04-21 20:21:45 -04:00
Look at the < live-example name = "dependency-injection-in-action" > < / live-example >
2017-02-22 13:09:39 -05:00
for the full source code.
2017-04-10 11:51:13 -04:00
< / div >
2017-03-27 11:08:53 -04:00
2017-02-22 13:09:39 -05:00
{@a tokens}
2018-05-31 15:02:50 -04:00
## Provider token alternatives: class interface and 'InjectionToken'
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
Angular dependency injection is easiest when the provider token is a class
that is also the type of the returned dependency object, or service.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
However, a token doesn't have to be a class and even when it is a class,
2017-02-22 13:09:39 -05:00
it doesn't have to be the same type as the returned object.
2017-03-31 19:57:13 -04:00
That's the subject of the next section.
{@a class-interface}
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
### Class interface
2017-03-31 19:57:13 -04:00
The previous *Hero of the Month* example used the `MinimalLogger` class
2018-05-31 15:02:50 -04:00
as the token for a provider of `LoggerService` .
2017-02-22 13:09:39 -05:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "use-existing" header = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
`MinimalLogger` is an abstract class.
2017-03-30 15:04:18 -04:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/minimal-logger.service.ts" header = "dependency-injection-in-action/src/app/minimal-logger.service.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
An abstract class is usually a base class that you can extend.
In this app, however there is no class that inherits from `MinimalLogger` .
2019-10-26 13:00:38 -04:00
The `LoggerService` and the `DateLoggerService` could have inherited from `MinimalLogger` ,
2018-05-31 15:02:50 -04:00
or they could have implemented it instead, in the manner of an interface.
2017-04-26 08:11:02 -04:00
But they did neither.
2018-05-31 15:02:50 -04:00
`MinimalLogger` is used only as a dependency injection token.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
When you use a class this way, it's called a *class interface* .
2018-07-23 20:11:30 -04:00
2020-10-22 14:16:51 -04:00
As mentioned in [DI Providers ](guide/dependency-injection-providers#di-and-interfaces ),
2018-05-31 15:02:50 -04:00
an interface is not a valid DI token because it is a TypeScript artifact that doesn't exist at run time.
Use this abstract class interface to get the strong typing of an interface,
and also use it as a provider token in the way you would a normal class.
2017-02-22 13:09:39 -05:00
2019-10-26 10:35:03 -04:00
A class interface should define *only* the members that its consumers are allowed to call.
2017-02-22 13:09:39 -05:00
Such a narrowing interface helps decouple the concrete class from its consumers.
2017-03-27 11:08:53 -04:00
2018-07-19 18:00:08 -04:00
< div class = "alert is-helpful" >
2017-03-27 11:08:53 -04:00
2017-03-31 19:57:13 -04:00
Using a class as an interface gives you the characteristics of an interface in a real JavaScript object.
2018-05-31 15:02:50 -04:00
To minimize memory cost, however, the class should have *no implementation* .
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function.
2017-02-22 13:09:39 -05:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/minimal-logger.service.ts" region = "minimal-logger-transpiled" header = "dependency-injection-in-action/src/app/minimal-logger.service.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
Notice that it doesn't have any members. It never grows no matter how many members you add to the class,
2018-07-23 20:11:30 -04:00
as long as those members are typed but not implemented.
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
Look again at the TypeScript `MinimalLogger` class to confirm that it has no implementation.
2017-02-22 13:09:39 -05:00
2017-04-10 11:51:13 -04:00
< / div >
2017-03-27 11:08:53 -04:00
2017-03-31 19:57:13 -04:00
{@a injection-token}
2018-05-31 15:02:50 -04:00
### 'InjectionToken' objects
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
Dependency objects can be simple values like dates, numbers and strings, or
2017-02-22 13:09:39 -05:00
shapeless objects like arrays and functions.
Such objects don't have application interfaces and therefore aren't well represented by a class.
2017-03-31 07:23:16 -04:00
They're better represented by a token that is both unique and symbolic,
a JavaScript object that has a friendly name but won't conflict with
2017-02-22 13:09:39 -05:00
another token that happens to have the same name.
2018-05-31 15:02:50 -04:00
`InjectionToken` has these characteristics.
2017-03-31 19:57:13 -04:00
You encountered them twice in the *Hero of the Month* example,
2017-02-22 13:09:39 -05:00
in the *title* value provider and in the *runnersUp* factory provider.
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "provide-injection-token" header = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
You created the `TITLE` token like this:
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region = "injection-token" header = "dependency-injection-in-action/src/app/hero-of-the-month.component.ts" > < / code-example >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
The type parameter, while optional, conveys the dependency's type to developers and tooling.
The token description is another developer aid.
2017-02-22 13:09:39 -05:00
{@a di-inheritance}
## Inject into a derived class
2018-05-31 15:02:50 -04:00
2017-03-31 19:57:13 -04:00
Take care when writing a component that inherits from another component.
2017-03-31 07:23:16 -04:00
If the base component has injected dependencies,
2017-03-31 19:57:13 -04:00
you must re-provide and re-inject them in the derived class
2017-02-22 13:09:39 -05:00
and then pass them down to the base class through the constructor.
2017-03-31 07:23:16 -04:00
In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent`
2017-02-22 13:09:39 -05:00
to display a *sorted* list of heroes.
2019-11-11 17:47:51 -05:00
< div class = "lightbox" >
< img src = "generated/images/guide/dependency-injection-in-action/sorted-heroes.png" alt = "Sorted Heroes" >
< / div >
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The `HeroesBaseComponent` can stand on its own.
It demands its own instance of `HeroService` to get heroes
2017-02-22 13:09:39 -05:00
and displays them in the order they arrive from the database.
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/sorted-heroes.component.ts" region = "heroes-base" header = "src/app/sorted-heroes.component.ts (HeroesBaseComponent)" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2018-07-19 18:00:08 -04:00
< div class = "alert is-helpful" >
2017-03-27 11:08:53 -04:00
2018-05-31 15:02:50 -04:00
### Keep constructors simple
2017-03-31 19:57:13 -04:00
2018-05-31 15:02:50 -04:00
Constructors should do little more than initialize variables.
2017-02-22 13:09:39 -05:00
This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server.
2017-03-31 19:57:13 -04:00
That's why you call the `HeroService` from within the `ngOnInit` rather than the constructor.
2017-02-22 13:09:39 -05:00
2017-04-10 11:51:13 -04:00
< / div >
2017-03-27 11:08:53 -04:00
2017-03-31 19:57:13 -04:00
2017-03-27 11:08:53 -04:00
Users want to see the heroes in alphabetical order.
2017-03-31 19:57:13 -04:00
Rather than modify the original component, sub-class it and create a
2017-02-22 13:09:39 -05:00
`SortedHeroesComponent` that sorts the heroes before presenting them.
The `SortedHeroesComponent` lets the base class fetch the heroes.
Unfortunately, Angular cannot inject the `HeroService` directly into the base class.
2017-03-31 19:57:13 -04:00
You must provide the `HeroService` again for *this* component,
2017-02-22 13:09:39 -05:00
then pass it down to the base class inside the constructor.
2017-03-30 15:04:18 -04:00
2018-10-11 07:29:59 -04:00
< code-example path = "dependency-injection-in-action/src/app/sorted-heroes.component.ts" region = "sorted-heroes" header = "src/app/sorted-heroes.component.ts (SortedHeroesComponent)" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
Now take note of the `afterGetHeroes()` method.
Your first instinct might have been to create an `ngOnInit` method in `SortedHeroesComponent` and do the sorting there.
2017-03-31 07:23:16 -04:00
But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit`
2017-03-31 19:57:13 -04:00
so you'd be sorting the heroes array *before they arrived* . That produces a nasty error.
2017-02-22 13:09:39 -05:00
2017-03-31 19:57:13 -04:00
Overriding the base class's `afterGetHeroes()` method solves the problem.
2017-02-22 13:09:39 -05:00
2017-03-31 07:23:16 -04:00
These complications argue for *avoiding component inheritance* .
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
{@a forwardref}
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
## Break circularities with a forward class reference (*forwardRef*)
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The order of class declaration matters in TypeScript.
You can't refer directly to a class until it's been defined.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
This isn't usually a problem, especially if you adhere to the recommended *one class per file* rule.
But sometimes circular references are unavoidable.
You're in a bind when class 'A' refers to class 'B' and 'B' refers to 'A'.
One of them has to be defined first.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The Angular `forwardRef()` function creates an *indirect* reference that Angular can resolve later.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
The *Parent Finder* sample is full of circular class references that are impossible to break.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
You face this dilemma when a class makes *a reference to itself*
as does `AlexComponent` in its `providers` array.
The `providers` array is a property of the `@Component()` decorator function which must
appear *above* the class definition.
2017-02-22 13:09:39 -05:00
2018-05-31 15:02:50 -04:00
Break the circularity with `forwardRef` .
2017-02-22 13:09:39 -05:00
2019-07-20 13:40:17 -04:00
< code-example path = "dependency-injection-in-action/src/app/parent-finder.component.ts" region = "alex-providers" header = "parent-finder.component.ts (AlexComponent providers)" > < / code-example >