From 2379ad1a4bc527be4b45bf6a697d9de1be481685 Mon Sep 17 00:00:00 2001 From: Judy Bogart Date: Thu, 31 May 2018 12:02:50 -0700 Subject: [PATCH] docs: edit and organize di guide (#21915) PR Close #21915 --- .../src/app/app.component.html | 4 + .../src/app/app.component.ts | 3 - .../src/app/app.module.ts | 2 + .../src/app/date-logger.service.ts | 4 +- .../src/app/hero.service.ts | 4 +- .../src/app/logger.service.ts | 4 +- .../src/app/storage.component.ts | 38 + .../src/app/storage.service.ts | 34 + .../src/app/user-context.service.ts | 4 +- .../src/app/user.service.ts | 4 +- .../src/app/heroes/hero.service.3.ts | 3 +- .../src/app/heroes/hero.service.4.ts | 3 +- .../src/app/heroes/heroes.component.1.ts | 1 + .../src/app/logger.service.ts | 4 +- .../src/app/user.service.ts | 4 +- .../src/app/app.module.ts | 15 +- .../src/app/car.services.ts | 24 +- .../src/app/hero-tax-return.component.ts | 4 +- .../src/app/heroes.service.ts | 4 +- .../guide/dependency-injection-in-action.md | 1045 +++++------------ .../guide/dependency-injection-navtree.md | 312 +++++ .../guide/dependency-injection-pattern.md | 167 --- .../guide/dependency-injection-providers.md | 353 ++++++ aio/content/guide/dependency-injection.md | 757 +++--------- .../hierarchical-dependency-injection.md | 246 ++-- aio/content/guide/providers.md | 2 +- aio/content/guide/upgrade.md | 2 +- aio/content/navigation.json | 19 +- 28 files changed, 1405 insertions(+), 1661 deletions(-) create mode 100644 aio/content/examples/dependency-injection-in-action/src/app/storage.component.ts create mode 100644 aio/content/examples/dependency-injection-in-action/src/app/storage.service.ts create mode 100644 aio/content/guide/dependency-injection-navtree.md delete mode 100644 aio/content/guide/dependency-injection-pattern.md create mode 100644 aio/content/guide/dependency-injection-providers.md diff --git a/aio/content/examples/dependency-injection-in-action/src/app/app.component.html b/aio/content/examples/dependency-injection-in-action/src/app/app.component.html index 45cc12bc2d..e69f4bed42 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/app.component.html +++ b/aio/content/examples/dependency-injection-in-action/src/app/app.component.html @@ -36,3 +36,7 @@
+ +
+ +
diff --git a/aio/content/examples/dependency-injection-in-action/src/app/app.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/app.component.ts index b29f3b4126..a1992e0892 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/app.component.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/app.component.ts @@ -9,9 +9,6 @@ import { UserService } from './user.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', -// #docregion providers - providers: [ LoggerService, UserContextService, UserService ] -// #enddocregion providers }) export class AppComponent { // #enddocregion import-services diff --git a/aio/content/examples/dependency-injection-in-action/src/app/app.module.ts b/aio/content/examples/dependency-injection-in-action/src/app/app.module.ts index 490670a71c..5c48d4f5f5 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/app.module.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/app.module.ts @@ -31,6 +31,7 @@ import { ParentFinderComponent, BarryComponent, BethComponent, BobComponent } from './parent-finder.component'; +import { StorageComponent } from './storage.component'; const declarations = [ AppComponent, @@ -63,6 +64,7 @@ const c_components = [ a_components, b_components, c_components, + StorageComponent, ], bootstrap: [ AppComponent ], // #docregion providers diff --git a/aio/content/examples/dependency-injection-in-action/src/app/date-logger.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/date-logger.service.ts index 465e16cf9b..d792018c56 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/date-logger.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/date-logger.service.ts @@ -5,7 +5,9 @@ import { Injectable } from '@angular/core'; import { LoggerService } from './logger.service'; // #docregion date-logger-service -@Injectable() +@Injectable({ + providedIn: 'root' +}) // #docregion date-logger-service-signature export class DateLoggerService extends LoggerService // #enddocregion date-logger-service-signature diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero.service.ts index 6eb5ffa14b..44db98ee7a 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero.service.ts @@ -2,7 +2,9 @@ import { Injectable } from '@angular/core'; import { Hero } from './hero'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class HeroService { // TODO: move to database diff --git a/aio/content/examples/dependency-injection-in-action/src/app/logger.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/logger.service.ts index df8ee6b9c7..824d84b672 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/logger.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/logger.service.ts @@ -1,7 +1,9 @@ // #docregion import { Injectable } from '@angular/core'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class LoggerService { logs: string[] = []; diff --git a/aio/content/examples/dependency-injection-in-action/src/app/storage.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/storage.component.ts new file mode 100644 index 0000000000..d0fb0ca54e --- /dev/null +++ b/aio/content/examples/dependency-injection-in-action/src/app/storage.component.ts @@ -0,0 +1,38 @@ +// #docregion +import { Component, OnInit, Self, SkipSelf } from '@angular/core'; +import { BROWSER_STORAGE, BrowserStorageService } from './storage.service'; + +@Component({ + selector: 'app-storage', + template: ` + Open the inspector to see the local/session storage keys: + +

Session Storage

+ + +

Local Storage

+ + `, + providers: [ + BrowserStorageService, + { provide: BROWSER_STORAGE, useFactory: () => sessionStorage } + ] +}) +export class StorageComponent implements OnInit { + + constructor( + @Self() private sessionStorageService: BrowserStorageService, + @SkipSelf() private localStorageService: BrowserStorageService, + ) { } + + ngOnInit() { + } + + setSession() { + this.sessionStorageService.set('hero', 'Mr. Nice - Session'); + } + + setLocal() { + this.localStorageService.set('hero', 'Mr. Nice - Local'); + } +} diff --git a/aio/content/examples/dependency-injection-in-action/src/app/storage.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/storage.service.ts new file mode 100644 index 0000000000..f90bbe01bc --- /dev/null +++ b/aio/content/examples/dependency-injection-in-action/src/app/storage.service.ts @@ -0,0 +1,34 @@ +// #docregion +import { Inject, Injectable, InjectionToken } from '@angular/core'; + +// #docregion storage-token +export const BROWSER_STORAGE = new InjectionToken('Browser Storage', { + providedIn: 'root', + factory: () => localStorage +}); +// #enddocregion storage-token + +// #docregion inject-storage-token +@Injectable({ + providedIn: 'root' +}) +export class BrowserStorageService { + constructor(@Inject(BROWSER_STORAGE) public storage: Storage) {} + + get(key: string) { + this.storage.getItem(key); + } + + set(key: string, value: string) { + this.storage.setItem(key, value); + } + + remove(key: string) { + this.storage.removeItem(key); + } + + clear() { + this.storage.clear(); + } +} +// #enddocregion inject-storage-token diff --git a/aio/content/examples/dependency-injection-in-action/src/app/user-context.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/user-context.service.ts index ed394fc734..45f24105b3 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/user-context.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/user-context.service.ts @@ -6,7 +6,9 @@ import { LoggerService } from './logger.service'; import { UserService } from './user.service'; // #docregion injectables, injectable -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class UserContextService { // #enddocregion injectables, injectable name: string; diff --git a/aio/content/examples/dependency-injection-in-action/src/app/user.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/user.service.ts index c48b025a08..09c1c7b4cd 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/user.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/user.service.ts @@ -1,7 +1,9 @@ // #docregion import { Injectable } from '@angular/core'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class UserService { getUserById(userId: number): any { diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.3.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.3.ts index 2d43704de0..9008bf15c3 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.3.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.3.ts @@ -1,11 +1,10 @@ // #docregion import { Injectable } from '@angular/core'; -import { HEROES } from './mock-heroes'; +import { HEROES } from './mock-heroes'; @Injectable({ // we declare that this service should be created // by the root application injector. - providedIn: 'root', }) export class HeroService { diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.4.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.4.ts index 0b77c78545..121f94ef02 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.4.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.4.ts @@ -1,12 +1,11 @@ // #docregion import { Injectable } from '@angular/core'; import { HeroModule } from './hero.module'; -import { HEROES } from './mock-heroes'; +import { HEROES } from './mock-heroes'; @Injectable({ // we declare that this service should be created // by any injector that includes HeroModule. - providedIn: HeroModule, }) export class HeroService { diff --git a/aio/content/examples/dependency-injection/src/app/heroes/heroes.component.1.ts b/aio/content/examples/dependency-injection/src/app/heroes/heroes.component.1.ts index 962f95fcb3..a9e40e902c 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/heroes.component.1.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/heroes.component.1.ts @@ -1,6 +1,7 @@ // #docplaster // #docregion, v1 import { Component } from '@angular/core'; + // #enddocregion v1 import { HeroService } from './hero.service'; diff --git a/aio/content/examples/dependency-injection/src/app/logger.service.ts b/aio/content/examples/dependency-injection/src/app/logger.service.ts index e943523ad2..3dd172c5f3 100644 --- a/aio/content/examples/dependency-injection/src/app/logger.service.ts +++ b/aio/content/examples/dependency-injection/src/app/logger.service.ts @@ -1,7 +1,9 @@ // #docregion import { Injectable } from '@angular/core'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class Logger { logs: string[] = []; // capture logs for testing diff --git a/aio/content/examples/dependency-injection/src/app/user.service.ts b/aio/content/examples/dependency-injection/src/app/user.service.ts index 03e23b7687..b665f7c5fa 100644 --- a/aio/content/examples/dependency-injection/src/app/user.service.ts +++ b/aio/content/examples/dependency-injection/src/app/user.service.ts @@ -11,7 +11,9 @@ export class User { let alice = new User('Alice', true); let bob = new User('Bob', false); -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class UserService { user = bob; // initial user is Bob diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/app.module.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/app.module.ts index 6ea18655af..6445bb0f11 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/app.module.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/app.module.ts @@ -3,23 +3,18 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; -import { HeroTaxReturnComponent } from './hero-tax-return.component'; -import { HeroesListComponent } from './heroes-list.component'; -import { HeroesService } from './heroes.service'; -import { VillainsListComponent } from './villains-list.component'; +import { AppComponent } from './app.component'; +import { HeroTaxReturnComponent } from './hero-tax-return.component'; +import { HeroesListComponent } from './heroes-list.component'; +import { VillainsListComponent } from './villains-list.component'; -import { carComponents, carServices } from './car.components'; +import { carComponents } from './car.components'; @NgModule({ imports: [ BrowserModule, FormsModule ], - providers: [ - carServices, - HeroesService - ], declarations: [ AppComponent, carComponents, diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/car.services.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/car.services.ts index 03c79270b0..9ce98d1c5f 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/car.services.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/car.services.ts @@ -21,13 +21,17 @@ export class Tires { } //// Engine services /// -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class EngineService { id = 'E1'; getEngine() { return new Engine(); } } -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class EngineService2 { id = 'E2'; getEngine() { @@ -38,14 +42,18 @@ export class EngineService2 { } //// Tire services /// -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class TiresService { id = 'T1'; getTires() { return new Tires(); } } /// Car Services /// -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class CarService { id = 'C1'; constructor( @@ -63,7 +71,9 @@ export class CarService { } } -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class CarService2 extends CarService { id = 'C2'; constructor( @@ -78,7 +88,9 @@ export class CarService2 extends CarService { } } -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class CarService3 extends CarService2 { id = 'C3'; constructor( diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.component.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.component.ts index 0696e5f96b..e1a8daf656 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.component.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.component.ts @@ -13,17 +13,19 @@ import { HeroTaxReturnService } from './hero-tax-return.service'; }) export class HeroTaxReturnComponent { message = ''; + @Output() close = new EventEmitter(); get taxReturn(): HeroTaxReturn { return this.heroTaxReturnService.taxReturn; } + @Input() set taxReturn (htr: HeroTaxReturn) { this.heroTaxReturnService.taxReturn = htr; } - constructor(private heroTaxReturnService: HeroTaxReturnService ) { } + constructor(private heroTaxReturnService: HeroTaxReturnService) { } onCanceled() { this.flashMessage('Canceled'); diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/heroes.service.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/heroes.service.ts index c4d08c7b8f..76b8ec09bb 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/heroes.service.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/heroes.service.ts @@ -4,7 +4,9 @@ import { Observable, Observer } from 'rxjs'; import { Hero, HeroTaxReturn } from './hero'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class HeroesService { heroes: Hero[] = [ { id: 1, name: 'RubberMan', tid: '082-27-5678'}, diff --git a/aio/content/guide/dependency-injection-in-action.md b/aio/content/guide/dependency-injection-in-action.md index 8d1c222a05..dbc11dc1ed 100644 --- a/aio/content/guide/dependency-injection-in-action.md +++ b/aio/content/guide/dependency-injection-in-action.md @@ -1,94 +1,33 @@ -# Dependency Injection +# Dependency Injection in Action - -Dependency Injection is a powerful pattern for managing code dependencies. -This cookbook explores many of the features of Dependency Injection (DI) in Angular. +This section explores many of the features of dependency injection (DI) in Angular. {@a toc} See the of the code in this cookbook. -{@a app-wide-dependencies} - -## Application-wide dependencies -Register providers for dependencies used throughout the application -in the `@Injectable()` decorator of the service itself. - - - - -`providedIn` here tells Angular that the root injector is responsible for creating an instance of the `HeroService`. -Services that are provided this way are automatically made available to the entire -application and don't need to be listed in any module. - - -Service classes can act as their own providers which is why defining them in the `@Injectable` decorator -is all the registration you need. - -
- - - -A *provider* is something that can create or deliver a service. -Angular creates a service instance from a class provider by using `new`. -Read more about providers in the [Dependency Injection](guide/dependency-injection#register-providers-ngmodule) -guide. - -
- - -Now that you've registered these services, -Angular can inject them into the constructor of *any* component or service, *anywhere* in the application. - - -{@a external-module-configuration} - - -## External module configuration -If a provider cannot be configured in the `@Injectable` decorator of the service, then register application-wide providers in the root `AppModule`, not in the `AppComponent`. Generally, register providers in the `NgModule` rather than in the root application component. - -Do this when users should explicitly opt-in to use a service, or the service should be -provided in a lazily-loaded context, -or when you are configuring another application global service _before the application starts_. - -Here is an example of the case where the component router configuration includes a non-default -[location strategy](guide/router#location-strategy) by listing its provider -in the `providers` list of the `AppModule`. - - - - - - - - -{@a injectable} - - {@a nested-dependencies} +## Nested service dependencies - -## _@Injectable()_ and nested service dependencies -The consumer of an injected service does not know how to create that service. -It shouldn't care. -It's the dependency injection's job to create and cache that service. +The _consumer_ of an injected service doesn't need to know how to create that service. +It's the job of the DI framework to create and cache dependencies. The consumer just +needs to let the DI framework know which dependencies it needs. Sometimes a service depends on other services, which may depend on yet other services. -Resolving these nested dependencies in the correct order is also the framework's job. -At each step, the consumer of dependencies simply declares what it requires in its -constructor and the framework takes over. +The dependency injection framework resolves these nested dependencies in the correct order. +At each step, the consumer of dependencies declares what it requires in its +constructor, and lets the framework provide them. -The following example shows injecting both the `LoggerService` and the `UserContext` in the `AppComponent`. +The following example shows that `AppComponent` declares its dependence on `LoggerService` and `UserContext`. - -The `UserContext` in turn has its own dependencies on both the `LoggerService` and -a `UserService` that gathers information about a particular user. +`UserContext` in turn depends on both `LoggerService` and +`UserService`, another service that gathers information about a particular user. @@ -96,108 +35,64 @@ a `UserService` that gathers information about a particular user. +When Angular creates `AppComponent`, the DI framework creates an instance of `LoggerService` and starts to create `UserContextService`. +`UserContextService` also needs `LoggerService`, which the framework already has, so the framework can provide the same instance. `UserContextService` also needs `UserService`, which the framework has yet to create. `UserService` has no further dependencies, so the framework can simply use `new` to instantiate the class and provide the instance to the `UserContextService` constructor. -When Angular creates the `AppComponent`, the dependency injection framework creates an instance of the `LoggerService` and -starts to create the `UserContextService`. -The `UserContextService` needs the `LoggerService`, which the framework already has, and the `UserService`, which it has yet to create. -The `UserService` has no dependencies so the dependency injection framework can just -use `new` to instantiate one. - -The beauty of dependency injection is that `AppComponent` doesn't care about any of this. -You simply declare what is needed in the constructor (`LoggerService` and `UserContextService`) -and the framework does the rest. - -Once all the dependencies are in place, the `AppComponent` displays the user information: +The parent `AppComponent` doesn't need to know about the dependencies of dependencies. +Declare what's needed in the constructor (in this case `LoggerService` and `UserContextService`) +and the framework resolves the nested dependencies. +When all dependencies are in place, `AppComponent` displays the user information.
Logged In User
- - -{@a injectable-1} - - -### *@Injectable()* -Notice the `@Injectable()`decorator on the `UserContextService` class. - - - - - - -The `@Injectable` decorator indicates that the Angular DI system is used to create one or more instances of `UserContextService`. - {@a service-scope} - ## Limit service scope to a component subtree -All injected service dependencies are singletons meaning that, -for a given dependency injector, there is only one instance of service. +An Angular application has multiple injectors, arranged in a tree hierarchy that parallels the component tree. +Each injector creates a singleton instance of a dependency. +That same instance is injected wherever that injector provides that service. +A particular service can be provided and created at any level of the injector hierarchy, +which means that there can be multiple instances of a service if it is provided by multiple injectors. -But an Angular application has multiple dependency injectors, arranged in a tree hierarchy that parallels the component tree. -So a particular service can be *provided* and created at any component level and multiple times -if provided in multiple components. - -By default, a service dependency provided in one component is visible to all of its child components and -Angular injects the same service instance into all child components that ask for that service. - -Accordingly, dependencies provided in the root `AppComponent` can be injected into *any* component *anywhere* in the application. - -That isn't always desirable. -Sometimes you want to restrict service availability to a particular region of the application. +Dependencies provided by the root injector can be injected into *any* component *anywhere* in the application. +In some cases, you might want to restrict service availability to a particular region of the application. +For instance, you might want to let users explicitly opt in to use a service, +rather than letting the root injector provide it automatically. You can limit the scope of an injected service to a *branch* of the application hierarchy by providing that service *at the sub-root component for that branch*. -This example shows how similar providing a service to a sub-root component is -to providing a service in the root `AppComponent`. The syntax is the same. -Here, the `HeroService` is available to the `HeroesBaseComponent` because it is in the `providers` array: +This example shows how to make a different instance of `HeroService` available to `HeroesBaseComponent` +by adding it to the `providers` array of the `@Component()` decorator of the sub-component. +When Angular creates `HeroesBaseComponent`, it also creates a new instance of `HeroService` +that is visible only to that component and its children, if any. - -When Angular creates the `HeroesBaseComponent`, it also creates a new instance of `HeroService` -that is visible only to the component and its children, if any. - -You could also provide the `HeroService` to a *different* component elsewhere in the application. -That would result in a *different* instance of the service, living in a *different* injector. +You could also provide `HeroService` to a different component elsewhere in the application. +That would result in a different instance of the service, living in a different injector.
- - Examples of such scoped `HeroService` singletons appear throughout the accompanying sample code, -including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`. +including `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`. Each of these components has its own `HeroService` instance managing its own independent collection of heroes. -
- - -
- - - -### Take a break! -This much Dependency Injection knowledge may be all that many Angular developers -ever need to build their applications. It doesn't always have to be more complicated. - - -
- {@a multiple-service-instances} ## Multiple service instances (sandboxing) -Sometimes you want multiple instances of a service at *the same level of the component hierarchy*. +Sometimes you want multiple instances of a service at *the same level* of the component hierarchy. A good example is a service that holds state for its companion component instance. You need a separate instance of the service for each component. @@ -205,39 +100,37 @@ Each service has its own work-state, isolated from the service-and-state of a di This is called *sandboxing* because each service and component instance has its own sandbox to play in. {@a hero-bios-component} -Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`. + +In this example, `HeroBiosComponent` presents three instances of `HeroBioComponent`. - Each `HeroBioComponent` can edit a single hero's biography. -A `HeroBioComponent` relies on a `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero. +`HeroBioComponent` relies on `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero. +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. -Clearly the three instances of the `HeroBioComponent` can't share the same `HeroCacheService`. -They'd be competing with each other to determine which hero to cache. - -Each `HeroBioComponent` gets its *own* `HeroCacheService` instance -by listing the `HeroCacheService` in its metadata `providers` array. +Instead, each `HeroBioComponent` gets its *own* `HeroCacheService` instance +by listing `HeroCacheService` in its metadata `providers` array. - -The parent `HeroBiosComponent` binds a value to the `heroId`. -The `ngOnInit` passes that `id` to the service, which fetches and caches the hero. +The parent `HeroBiosComponent` binds a value to `heroId`. +`ngOnInit` passes that ID to the service, which fetches and caches the hero. The getter for the `hero` property pulls the cached hero from the service. -And the template displays this data-bound property. +The template displays this data-bound property. Find this example in live code and confirm that the three `HeroBioComponent` instances have their own cached hero data. @@ -246,104 +139,97 @@ and confirm that the three `HeroBioComponent` instances have their own cached he Bios +{@a qualify-dependency-lookup} +## Qualify dependency lookup with parameter decorators + +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, +starting at the component's local injector of the component, and if necessary bubbling up +through the injector tree until it reaches the root injector. + +* The first injector configured with a provider supplies the the dependency (a service instance or value) to the constructor. + +* If no provider is found in the root injector, the DI framework returns null to the constructor. + +There are a number of options for modifying the default search behavior, using _parameter decorators_ +on the service-valued parameters of a class constructor. {@a optional} +### Make a dependency `@Optional` and limit search with `@Host` -{@a qualify-dependency-lookup} +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. +Angular throws an error if it can't find the dependency during that walk. +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 +decorators on a service-valued parameter of the component's constructor. +* The `@Optional` property decorator tells Angular to return null when it can't find the dependency. -## Qualify dependency lookup with _@Optional()_ and `@Host()` -As you now know, dependencies can be registered at any level in the component hierarchy. +* 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, +that parent component becomes the host. The following example covers this second case. -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. Angular throws an error if it can't find the dependency during that walk. - -You *want* this behavior most of the time. -But sometimes you need to limit the search and/or accommodate a missing dependency. -You can modify Angular's search behavior with the `@Host` and `@Optional` qualifying decorators, -used individually or together. - -The `@Optional` decorator tells Angular to continue when it can't find the dependency. -Angular sets the injection parameter to `null` instead. - -The `@Host` decorator stops the upward search at the *host component*. - -The host component is typically the component requesting the dependency. -But when this component is projected into a *parent* component, that parent component becomes the host. -The next example covers this second case. - - -{@a demonstration} - - -### Demonstration -The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that you looked at [above](guide/dependency-injection-in-action#hero-bios-component). +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). - - Focus on the template: - - -Now there is a new `` element between the `` tags. +Now there's a new `` element between the `` tags. Angular *projects*, or *transcludes*, the corresponding `HeroContactComponent` into the `HeroBioComponent` view, -placing it in the `` slot of the `HeroBioComponent` template: +placing it in the `` slot of the `HeroBioComponent` template. - - -It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description: +The result is shown below, with the hero's telephone number from `HeroContactComponent` projected above the hero description.
bio and contact
- -Here's the `HeroContactComponent` which demonstrates the qualifying decorators: +Here's `HeroContactComponent`, which demonstrates the qualifying decorators. - - -Focus on the constructor parameters: +Focus on the constructor parameters. - - -The `@Host()` function decorating the `heroCache` property ensures that +The `@Host()` function decorating the `heroCache` constructor property ensures that 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 -in the component tree happens to have it. +in the component tree includes it. -A second `@Host()` function decorates the `loggerService` property. +A second `@Host()` function decorates the `loggerService` constructor property. The only `LoggerService` instance in the app is provided at the `AppComponent` level. The host `HeroBioComponent` doesn't have its own `LoggerService` provider. -Angular would throw an error if you hadn't also decorated the property with the `@Optional()` function. -Thanks to `@Optional()`, Angular sets the `loggerService` to null and the rest of the component adapts. +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. -Here's the `HeroBiosAndContactsComponent` in action. +Here's `HeroBiosAndContactsComponent` in action.
Bios with contact into @@ -351,43 +237,62 @@ Here's the `HeroBiosAndContactsComponent` in action. -If you comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree -until it finds the logger at the `AppComponent` level. The logger logic kicks in and the hero display updates -with the gratuitous "!!!", indicating that the logger was found. +If you comment out the `@Host()` decorator, Angular walks up the injector ancestor tree +until it finds the logger at the `AppComponent` level. +The logger logic kicks in and the hero display updates +with the "!!!" marker to indicate that the logger was found.
Without @Host
+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. -On the other hand, if you restore the `@Host()` decorator and comment out `@Optional`, -the application fails for lack of the required logger at the host component level. -
`EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)` + +### Supply a custom provider with `@Inject` + +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`. + + + + + +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 overriden during testing with a mock API of `localStorage` instead of interactive with real browser APIs. + +### 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. + + + + + +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` browswer API. + {@a component-element} ## Inject the component's DOM element -On occasion you might need to access a component's corresponding DOM element. -Although developers strive to avoid it, many visual effects and 3rd party tools, such as jQuery, +Although developers strive to avoid it, many visual effects and third-party tools, such as jQuery, require DOM access. +As a result, you might need to access a component's DOM element. -To illustrate, here's a simplified version of the `HighlightDirective` from +To illustrate, here's a simplified version of `HighlightDirective` from the [Attribute Directives](guide/attribute-directives) page. - - The directive sets the background to a highlight color when the user mouses over the -DOM element to which it is applied. +DOM element to which the directive is applied. -Angular sets the constructor's `el` parameter to the injected `ElementRef`, which is -a wrapper around that DOM element. -Its `nativeElement` property exposes the DOM element for the directive to manipulate. +Angular sets the constructor's `el` parameter to the injected `ElementRef`. +(An `ElementRef` is a wrapper around a DOM element, +whose `nativeElement` property exposes the DOM element for the directive to manipulate.) The sample code applies the directive's `myHighlight` attribute to two `
` tags, first without a value (yielding the default color) and then with an assigned color value. @@ -397,7 +302,6 @@ first without a value (yielding the default color) and then with an assigned col - The following image shows the effect of mousing over the `` tag.
@@ -411,12 +315,12 @@ The following image shows the effect of mousing over the ` @@ -424,37 +328,25 @@ Here's a typical example: - -Angular asks the injector for the service associated with the `LoggerService` +Angular asks the injector for the service associated with `LoggerService` and assigns the returned value to the `logger` parameter. -Where did the injector get that value? -It may already have that value in its internal container. -If it doesn't, it may be able to make one with the help of a ***provider***. -A *provider* is a recipe for delivering a service associated with a *token*. +If the injector has already cached an instance of the service associated with the token, +it provides that instance. +If it doesn't, it needs to make one using the provider associated with the token.
- - -If the injector doesn't have a provider for the requested *token*, it delegates the request +If the injector doesn't have a provider for a requested token, it delegates the request to its parent injector, where the process repeats until there are no more injectors. -If the search is futile, the injector throws an error—unless the request was [optional](guide/dependency-injection-in-action#optional). +If the search fails, the injector throws an error—unless the request was [optional](guide/dependency-injection-in-action#optional).
- - A new injector has no providers. -Angular initializes the injectors it creates with some providers it cares about. -You have to register your _own_ application providers manually, -usually in the `@Injectable` decorator of the service, `providers` array of the `NgModule` or `Directive` metadata: - - - - - +Angular initializes the injectors it creates with a set of preferred providers. +You have to configure providers for your own app-specific dependencies. {@a defining-providers} @@ -462,348 +354,272 @@ usually in the `@Injectable` decorator of the service, `providers` array of the ### Defining providers -The simple way of defining providers in the `@Injectable` decorator of the class is recommended. - - - - -Another alternative is to mention the class in the providers array of the `@NgModule` and you're done. - - - - - - -It's that simple because the most common injected service is an instance of a class. -But not every dependency can be satisfied by creating a new instance of a class. -You need other ways to deliver dependency values and that means you need other ways to specify a provider. - -The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them. +A dependency can't always be created by the default method of instantiating a class. +You learned about some other methods in [Dependency Providers](guide/dependency-injection-providers). +The following `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them. It's visually simple: a few properties and the logs produced by a logger.
Hero of the month
- - -The code behind it gives you plenty to think about. +The code behind it customizes how and where the DI framework provides dependencies. +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. - - - -{@a provide} - - -#### The *provide* object literal - -The `provide` object literal takes a *token* and a *definition object*. -The *token* is usually a class but [it doesn't have to be](guide/dependency-injection-in-action#tokens). - -The *definition* object has a required property that specifies how to create the singleton instance of the service. In this case, the property. - - +The `providers` array shows how you might use the different provider-definition keys; +`useValue`, `useClass`, `useExisting`, or `useFactory`. {@a usevalue} -#### useValue—the *value provider* - -Set the `useValue` property to a ***fixed value*** that the provider can return as the service instance (AKA, the "dependency object"). +#### Value providers: `useValue` +The `useValue` key lets you associate a fixed value with a DI token. Use this technique to provide *runtime configuration constants* such as website base addresses and feature flags. -You can use a *value provider* in a unit test to replace a production service with a fake or mock. +You can also use a value provider in a unit test to provide mock data in place of a production data service. -The `HeroOfTheMonthComponent` example has two *value providers*. -The first provides an instance of the `Hero` class; -the second specifies a literal string resource: +The `HeroOfTheMonthComponent` example has two value providers. +* The first provides an existing instance of the `Hero` class to use for the `Hero` token, rather than +requiring the injector to create a new instance with `new` or use its own cached instance. +Here, the token is the class itself. +* 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 +an `InjectionToken` instance. -The `Hero` provider token is a class which makes sense because the value is a `Hero` -and the consumer of the injected hero would want the type information. - -The `TITLE` provider token is *not a class*. -It's a special kind of provider lookup key called an [InjectionToken](guide/dependency-injection-in-action#injection-token). -You can use an `InjectionToken` for any kind of provider but it's particular +You can use an injection token for any kind of provider but it's particularly helpful when the dependency is a simple value like a string, a number, or a function. -The value of a *value provider* must be defined *now*. You can't create the value later. -Obviously the title string literal is immediately available. -The `someHero` variable in this example was set earlier in the file: +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. - - -The other providers create their values *lazily* when they're needed for injection. - - +Other types of providers can create their values *lazily*; that is, when they're needed for injection. {@a useclass} -#### useClass—the *class provider* +#### Class providers: `useClass` -The `useClass` provider creates and returns new instance of the specified class. +The `useClass` provider key lets you create and return a new instance of the specified class. -Use this technique to ***substitute an alternative implementation*** for a common or default class. -The alternative could implement a different strategy, extend the default class, -or fake the behavior of the real class in a test case. +You can use this type of provider to substitute an *alternative implementation* +for a common or default class. +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. -Here are two examples in the `HeroOfTheMonthComponent`: +The following code shows two examples in `HeroOfTheMonthComponent`. - - The first provider is the *de-sugared*, expanded form of the most typical case in which the class to be created (`HeroService`) is also the provider's dependency injection token. -It's in this long form to de-mystify the preferred short form. +The short form is generally preferred; this long form makes the details explicit. -The second provider substitutes the `DateLoggerService` for the `LoggerService`. -The `LoggerService` is already registered at the `AppComponent` level. -When _this component_ requests the `LoggerService`, it receives the `DateLoggerService` instead. +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.
- - -This component and its tree of child components receive the `DateLoggerService` instance. +This component and its tree of child components receive `DateLoggerService` instance. Components outside the tree continue to receive the original `LoggerService` instance.
- - -The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message: +`DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message: - - - {@a useexisting} +#### Alias providers: `useExisting` -#### _useExisting_—the *alias provider* - -The `useExisting` provider maps 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***. +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. +You can use this technique to narrow an API through an aliasing interface. +The following example shows an alias introduced for that purpose. - -Narrowing an API through an aliasing interface is _one_ important use case for this technique. -The following example shows aliasing for that purpose. - -Imagine that the `LoggerService` had a large API, much larger than the actual three methods and a property. +Imagine that `LoggerService` had a large API, much larger than the actual three methods and a property. You might want to shrink that API surface to just the members you actually need. -Here the `MinimalLogger` [*class-interface*](guide/dependency-injection-in-action#class-interface) reduces the API to two members: +In this example, the `MinimalLogger` [class-interface](#class-interface) reduces the API to two members: - - -Now put it to use in a simplified version of the `HeroOfTheMonthComponent`. +The following example puts `MinimalLogger` to use in a simplified version of `HeroOfTheMonthComponent`. - - -The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger` so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor: +The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger`, so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor.
MinimalLogger restricted API
- -Behind the scenes, Angular actually sets the `logger` parameter to the full service registered under the `LoggingService` token which happens to be the `DateLoggerService` that was [provided above](guide/dependency-injection-in-action#useclass). +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).
- - -The following image, which displays the logging date, confirms the point: +This is illustrated in the following image, which displays the logging date.
DateLoggerService entry
- -
- - - {@a usefactory} +#### Factory providers: `useFactory` -#### _useFactory_—the *factory provider* - -The `useFactory` provider creates a dependency object by calling a factory function -as in this example. +The `useFactory` provider key lets you create a dependency object by calling a factory function, +as in the following example. +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. +Use this technique to create a dependency object with a factory function +whose inputs are a combination of *injected services* and *local state*. -Use this technique to ***create a dependency object*** -with a factory function whose inputs are some ***combination of injected services and local state***. - -The *dependency object* doesn't have to be a class instance. It could be anything. -In this example, the *dependency object* is a string of the names of the runners-up +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 to the "Hero of the Month" contest. -The local state is the number `2`, the number of runners-up this component should show. -It executes `runnersUpFactory` immediately with `2`. - -The `runnersUpFactory` itself isn't the provider factory function. -The true provider factory function is the function that `runnersUpFactory` returns. +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`. +The provider factory function (returned by `runnersUpFactory()`) returns the actual dependency object, +the string of names. - -That returned function takes a winning `Hero` and a `HeroService` as arguments. - +* The function takes a winning `Hero` and a `HeroService` as arguments. Angular supplies these arguments from injected values identified by the two *tokens* in the `deps` array. -The two `deps` values are *tokens* that the injector uses -to provide these factory function dependencies. - -After some undisclosed work, the function returns the string of names -and Angular injects it into the `runnersUp` parameter of the `HeroOfTheMonthComponent`. +* The function returns the string of names, which Angular than injects into +the `runnersUp` parameter of `HeroOfTheMonthComponent`.
- - The function retrieves candidate heroes from the `HeroService`, takes `2` of them to be the runners-up, and returns their concatenated names. Look at the for the full source code. -
- - {@a tokens} +## Provider token alternatives: class interface and 'InjectionToken' +Angular dependency injection is easiest when the provider token is a class +that is also the type of the returned dependency object, or service. -## Provider token alternatives: the *class-interface* and *InjectionToken* - -Angular dependency injection is easiest when the provider *token* is a class -that is also the type of the returned dependency object, or what you usually call the *service*. - -But the token doesn't have to be a class and even when it is a class, +However, a token doesn't have to be a class and even when it is a class, it doesn't have to be the same type as the returned object. That's the subject of the next section. {@a class-interface} -### class-interface +### Class interface + The previous *Hero of the Month* example used the `MinimalLogger` class -as the token for a provider of a `LoggerService`. +as the token for a provider of `LoggerService`. - - -The `MinimalLogger` is an abstract class. +`MinimalLogger` is an abstract class. - - -You usually inherit from an abstract class. -But *no class* in this application inherits from `MinimalLogger`. - -The `LoggerService` and the `DateLoggerService` _could_ have inherited from `MinimalLogger`. -They could have _implemented_ it instead in the manner of an interface. +An abstract class is usually a base class that you can extend. +In this app, however there is no class that inherits from `MinimalLogger`. +The `LoggerService` and the `DateLoggerService`could have inherited from `MinimalLogger`, +or they could have implemented it instead, in the manner of an interface. But they did neither. -The `MinimalLogger` is used exclusively as a dependency injection token. +`MinimalLogger` is used only as a dependency injection token. -When you use a class this way, it's called a ***class-interface***. -The key benefit of a *class-interface* is that you can get the strong-typing of an interface -and you can ***use it as a provider token*** in the way you would a normal class. +When you use a class this way, it's called a *class interface*. + +As mentioned in [DI Providers](guide/dependency-injection-providers#interface-not-valid-token), +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. -A ***class-interface*** should define *only* the members that its consumers are allowed to call. +A class interface should define *only* the members that its consumers are allowed to call. Such a narrowing interface helps decouple the concrete class from its consumers.
- - -#### Why *MinimalLogger* is a class and not a TypeScript interface -You can't use an interface as a provider token because -interfaces are not JavaScript objects. -They exist only in the TypeScript design space. -They disappear after the code is transpiled to JavaScript. - -A provider token must be a real JavaScript object of some kind: -such as a function, an object, a string, or a class. - Using a class as an interface gives you the characteristics of an interface in a real JavaScript object. - -Of course a real object occupies memory. To minimize memory cost, the class should have *no implementation*. -The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function: +To minimize memory cost, however, the class should have *no implementation*. +The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function. +Notice that it doesn't have any members. It never grows no matter how many members you add to the class, +as long as those members are typed but not implemented. - -Notice that it doesn't have a single member. It never grows no matter how many members you add to the class *as long as those members are typed but not implemented*. Look again at the TypeScript `MinimalLogger` class to confirm that it has no implementation. - +Look again at the TypeScript `MinimalLogger` class to confirm that it has no implementation.
- {@a injection-token} -### _InjectionToken_ +### 'InjectionToken' objects Dependency objects can be simple values like dates, numbers and strings, or shapeless objects like arrays and functions. @@ -813,7 +629,7 @@ 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 another token that happens to have the same name. -The `InjectionToken` has these characteristics. +`InjectionToken` has these characteristics. You encountered them twice in the *Hero of the Month* example, in the *title* value provider and in the *runnersUp* factory provider. @@ -821,25 +637,20 @@ in the *title* value provider and in the *runnersUp* factory provider. - - You created the `TITLE` token like this: - - The type parameter, while optional, conveys the dependency's type to developers and tooling. The token description is another developer aid. {@a di-inheritance} - - ## Inject into a derived class + Take care when writing a component that inherits from another component. If the base component has injected dependencies, you must re-provide and re-inject them in the derived class @@ -848,37 +659,30 @@ and then pass them down to the base class through the constructor. In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent` to display a *sorted* list of heroes. -
Sorted Heroes
- - -The `HeroesBaseComponent` could stand on its own. -It demands its own instance of the `HeroService` to get heroes +The `HeroesBaseComponent` can stand on its own. +It demands its own instance of `HeroService` to get heroes and displays them in the order they arrive from the database. - -
+### Keep constructors simple - -***Keep constructors simple.*** They should do little more than initialize variables. +Constructors should do little more than initialize variables. This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server. That's why you call the `HeroService` from within the `ngOnInit` rather than the constructor. -
- Users want to see the heroes in alphabetical order. Rather than modify the original component, sub-class it and create a `SortedHeroesComponent` that sorts the heroes before presenting them. @@ -894,7 +698,6 @@ then pass it down to the base class inside the constructor. - 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. But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit` @@ -905,331 +708,8 @@ Overriding the base class's `afterGetHeroes()` method solves the problem. These complications argue for *avoiding component inheritance*. -{@a find-parent} - - - -## Find a parent component by injection - -Application components often need to share information. -More loosely coupled techniques such as data binding and service sharing -are preferable. But sometimes it makes sense for one component -to have a direct reference to another component -perhaps to access values or call methods on that component. - -Obtaining a component reference is a bit tricky in Angular. -Although an Angular application is a tree of components, -there is no public API for inspecting and traversing that tree. - -There is an API for acquiring a child reference. -Check out `Query`, `QueryList`, `ViewChildren`, and `ContentChildren` -in the [API Reference](api/). - -There is no public API for acquiring a parent reference. -But because every component instance is added to an injector's container, -you can use Angular dependency injection to reach a parent component. - -This section describes some techniques for doing that. - - -{@a known-parent} - - -### Find a parent component of known type - -You use standard class injection to acquire a parent component whose type you know. - -In the following example, the parent `AlexComponent` has several children including a `CathyComponent`: - -{@a alex} - - - - - - - - -*Cathy* reports whether or not she has access to *Alex* -after injecting an `AlexComponent` into her constructor: - - - - - - - -Notice that even though the [@Optional](guide/dependency-injection-in-action#optional) qualifier -is there for safety, -the -confirms that the `alex` parameter is set. - - -{@a base-parent} - - -### Cannot find a parent by its base class - -What if you *don't* know the concrete parent component class? - -A re-usable component might be a child of multiple components. -Imagine a component for rendering breaking news about a financial instrument. -For business reasons, this news component makes frequent calls -directly into its parent instrument as changing market data streams by. - -The app probably defines more than a dozen financial instrument components. -If you're lucky, they all implement the same base class -whose API your `NewsComponent` understands. - - -
- - - -Looking for components that implement an interface would be better. -That's not possible because TypeScript interfaces disappear -from the transpiled JavaScript, which doesn't support interfaces. -There's no artifact to look for. - -
- - - -This isn't necessarily good design. -This example is examining *whether a component can -inject its parent via the parent's base class*. - -The sample's `CraigComponent` explores this question. [Looking back](guide/dependency-injection-in-action#alex), -you see that the `Alex` component *extends* (*inherits*) from a class named `Base`. - - - - - - - -The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded. - - - - - - - -Unfortunately, this does not work. -The -confirms that the `alex` parameter is null. -*You cannot inject a parent by its base class.* - - - -{@a class-interface-parent} - - -### Find a parent by its class-interface - -You can find a parent component with a [class-interface](guide/dependency-injection-in-action#class-interface). - -The parent must cooperate by providing an *alias* to itself in the name of a *class-interface* token. - -Recall that Angular always adds a component instance to its own injector; -that's why you could inject *Alex* into *Cathy* [earlier](guide/dependency-injection-in-action#known-parent). - -Write an [*alias provider*](guide/dependency-injection-in-action#useexisting)—a `provide` object literal with a `useExisting` -definition—that creates an *alternative* way to inject the same component instance -and add that provider to the `providers` array of the `@Component` metadata for the `AlexComponent`: - -{@a alex-providers} - - - - - - - - -[Parent](guide/dependency-injection-in-action#parent-token) is the provider's *class-interface* token. -The [*forwardRef*](guide/dependency-injection-in-action#forwardref) breaks the circular reference you just created by having the `AlexComponent` refer to itself. - -*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, -the same way you've done it before: - - - - - - - -Here's *Alex* and family in action: - -
- Alex in action -
- - - -{@a parent-tree} - - -### Find the parent in a tree of parents with _@SkipSelf()_ - -Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*. -Both *Alice* and *Barry* implement the `Parent` *class-interface*. - -*Barry* is the problem. He needs to reach his parent, *Alice*, and also be a parent to *Carol*. -That means he must both *inject* the `Parent` *class-interface* to get *Alice* and -*provide* a `Parent` to satisfy *Carol*. - -Here's *Barry*: - - - - - - - -*Barry*'s `providers` array looks just like [*Alex*'s](guide/dependency-injection-in-action#alex-providers). -If you're going to keep writing [*alias providers*](guide/dependency-injection-in-action#useexisting) like this you should create a [helper function](guide/dependency-injection-in-action#provideparent). - -For now, focus on *Barry*'s constructor: - - - - - - - - - - - - - - - - - -It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator. - -`@SkipSelf` is essential for two reasons: - -1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself, -which *is* what parent means. - -2. Angular throws a cyclic dependency error if you omit the `@SkipSelf` decorator. - - `Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)` - -Here's *Alice*, *Barry* and family in action: - - -
- Alice in action -
- - - -{@a parent-token} - - -### The *Parent* class-interface -You [learned earlier](guide/dependency-injection-in-action#class-interface) that a *class-interface* is an abstract class used as an interface rather than as a base class. - -The example defines a `Parent` *class-interface*. - - - - - - - -The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*. -The `name` property is the only member of a parent component that a child component can call. -Such a narrow interface helps decouple the child component class from its parent components. - -A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does: - - - - - - - -Doing so adds clarity to the code. But it's not technically necessary. -Although the `AlexComponent` has a `name` property, as required by its `Base` class, -its class signature doesn't mention `Parent`: - - - - - - - -
- - - -The `AlexComponent` *should* implement `Parent` as a matter of proper style. -It doesn't in this example *only* to demonstrate that the code will compile and run without the interface - - -
- - - -{@a provideparent} - - -### A _provideParent()_ helper function - -Writing variations of the same parent *alias provider* gets old quickly, -especially this awful mouthful with a [*forwardRef*](guide/dependency-injection-in-action#forwardref): - - - - - - - -You can extract that logic into a helper function like this: - - - - - - - -Now you can add a simpler, more meaningful parent provider to your components: - - - - - - - -You can do better. The current version of the helper function can only alias the `Parent` *class-interface*. -The application might have a variety of parent types, each with its own *class-interface* token. - -Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*. - - - - - - - -And here's how you could use it with a different parent type: - - - - - - - {@a forwardref} - - ## Break circularities with a forward class reference (*forwardRef*) The order of class declaration matters in TypeScript. @@ -1244,17 +724,64 @@ The Angular `forwardRef()` function creates an *indirect* reference that Angular The *Parent Finder* sample is full of circular class references that are impossible to break. - You face this dilemma when a class makes *a reference to itself* -as does the `AlexComponent` in its `providers` array. -The `providers` array is a property of the `@Component` decorator function which must +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. -Break the circularity with `forwardRef`: +Break the circularity with `forwardRef`. + diff --git a/aio/content/guide/dependency-injection-navtree.md b/aio/content/guide/dependency-injection-navtree.md new file mode 100644 index 0000000000..3db20c709b --- /dev/null +++ b/aio/content/guide/dependency-injection-navtree.md @@ -0,0 +1,312 @@ +# Navigate the component tree with DI + +Application components often need to share information. +You can often use loosely coupled techniques for sharing information, +such as data binding and service sharing, +but sometimes it makes sense for one component to have a direct reference to another component. +You need a direct reference, for instance, to access values or call methods on that component. + +Obtaining a component reference is a bit tricky in Angular. +Angular components themselves do not have a tree that you can +inspect or navigate programmatically. The parent-child relationship is indirect, +established through the components' [view objects](guide/glossary#view). + +Each component has a *host view*, and can have additional *embedded views*. +An embedded view in component A is the +host view of component B, which can in turn have embedded view. +This means that there is a [view hierarchy](guide/glossary#view-hierarchy) for each component, +of which that component's host view is the root. + +There is an API for navigating *down* the view hierarchy. +Check out `Query`, `QueryList`, `ViewChildren`, and `ContentChildren` +in the [API Reference](api/). + +There is no public API for acquiring a parent reference. +However, because every component instance is added to an injector's container, +you can use Angular dependency injection to reach a parent component. + +This section describes some techniques for doing that. + +{@a find-parent} +{@a known-parent} + + +### Find a parent component of known type + +You use standard class injection to acquire a parent component whose type you know. + +In the following example, the parent `AlexComponent` has several children including a `CathyComponent`: + +{@a alex} + + + + + + + + +*Cathy* reports whether or not she has access to *Alex* +after injecting an `AlexComponent` into her constructor: + + + + + + + +Notice that even though the [@Optional](guide/dependency-injection-in-action#optional) qualifier +is there for safety, +the +confirms that the `alex` parameter is set. + + +{@a base-parent} + + +### Unable to find a parent by its base class + +What if you *don't* know the concrete parent component class? + +A re-usable component might be a child of multiple components. +Imagine a component for rendering breaking news about a financial instrument. +For business reasons, this news component makes frequent calls +directly into its parent instrument as changing market data streams by. + +The app probably defines more than a dozen financial instrument components. +If you're lucky, they all implement the same base class +whose API your `NewsComponent` understands. + + +
+ + + +Looking for components that implement an interface would be better. +That's not possible because TypeScript interfaces disappear +from the transpiled JavaScript, which doesn't support interfaces. +There's no artifact to look for. + +
+ + + +This isn't necessarily good design. +This example is examining *whether a component can +inject its parent via the parent's base class*. + +The sample's `CraigComponent` explores this question. [Looking back](#alex), +you see that the `Alex` component *extends* (*inherits*) from a class named `Base`. + + + + + + + +The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded. + + + + + + + +Unfortunately, this does'nt work. +The +confirms that the `alex` parameter is null. +*You cannot inject a parent by its base class.* + + + +{@a class-interface-parent} + + +### Find a parent by its class interface + +You can find a parent component with a [class interface](guide/dependency-injection-in-action#class-interface). + +The parent must cooperate by providing an *alias* to itself in the name of a class interface token. + +Recall that Angular always adds a component instance to its own injector; +that's why you could inject *Alex* into *Cathy* [earlier](#known-parent). + +Write an [*alias provider*](guide/dependency-injection-in-action#useexisting)—a `provide` object literal with a `useExisting` +definition—that creates an *alternative* way to inject the same component instance +and add that provider to the `providers` array of the `@Component()` metadata for the `AlexComponent`. + +{@a alex-providers} + + + + + + + +[Parent](#parent-token) is the provider's class interface token. +The [*forwardRef*](guide/dependency-injection-in-action#forwardref) breaks the circular reference you just created by having the `AlexComponent` refer to itself. + +*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, +the same way you've done it before. + + + + + + + +Here's *Alex* and family in action. + +
+ Alex in action +
+ + + +{@a parent-tree} + + +### Find a parent in a tree with _@SkipSelf()_ + +Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*. +Both *Alice* and *Barry* implement the `Parent' class interface. + +*Barry* is the problem. He needs to reach his parent, *Alice*, and also be a parent to *Carol*. +That means he must both *inject* the `Parent` class interface to get *Alice* and +*provide* a `Parent` to satisfy *Carol*. + +Here's *Barry*. + + + + + + + +*Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers). +If you're going to keep writing [*alias providers*](guide/dependency-injection-in-action#useexisting) like this you should create a [helper function](#provideparent). + +For now, focus on *Barry*'s constructor. + + + + + + + + + + + + + + +It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator. + +`@SkipSelf` is essential for two reasons: + +1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself, +which *is* what parent means. + +2. Angular throws a cyclic dependency error if you omit the `@SkipSelf` decorator. + + `Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)` + +Here's *Alice*, *Barry*, and family in action. + + +
+ Alice in action +
+ + + +{@a parent-token} + + +### Parent class interface +You [learned earlier](guide/dependency-injection-in-action#class-interface) that a class interface is an abstract class used as an interface rather than as a base class. + +The example defines a `Parent` class interface. + + + + + + + +The `Parent` class interface defines a `name` property with a type declaration but *no implementation*. +The `name` property is the only member of a parent component that a child component can call. +Such a narrow interface helps decouple the child component class from its parent components. + +A component that could serve as a parent *should* implement the class interface as the `AliceComponent` does. + + + + + + + +Doing so adds clarity to the code. But it's not technically necessary. +Although `AlexComponent` has a `name` property, as required by its `Base` class, +its class signature doesn't mention `Parent`. + + + + + + + +
+ + + +`AlexComponent` *should* implement `Parent` as a matter of proper style. +It doesn't in this example *only* to demonstrate that the code will compile and run without the interface. + + +
+ + + +{@a provideparent} + + +### `provideParent()` helper function + +Writing variations of the same parent *alias provider* gets old quickly, +especially this awful mouthful with a [*forwardRef*](guide/dependency-injection-in-action#forwardref). + + + + + +You can extract that logic into a helper function like the following. + + + + + +Now you can add a simpler, more meaningful parent provider to your components. + + + + + + +You can do better. The current version of the helper function can only alias the `Parent` class interface. +The application might have a variety of parent types, each with its own class interface token. + +Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface. + + + + + + +And here's how you could use it with a different parent type. + + + + + diff --git a/aio/content/guide/dependency-injection-pattern.md b/aio/content/guide/dependency-injection-pattern.md deleted file mode 100644 index 93fd0eb113..0000000000 --- a/aio/content/guide/dependency-injection-pattern.md +++ /dev/null @@ -1,167 +0,0 @@ -# The Dependency Injection pattern - -**Dependency injection** is an important application design pattern. -It's used so widely that almost everyone just calls it _DI_. - -Angular has its own dependency injection framework, and -you really can't build an Angular application without it. - -This page covers what DI is and why it's useful. - -When you've learned the general pattern, you're ready to turn to -the [Angular Dependency Injection](guide/dependency-injection) guide to see how it works in an Angular app. - -{@a why-di } - -## Why dependency injection? - -To understand why dependency injection is so important, consider an example without it. -Imagine writing the following code: - - - - -The `Car` class creates everything it needs inside its constructor. -What's the problem? -The problem is that the `Car` class is brittle, inflexible, and hard to test. - -This `Car` needs an engine and tires. Instead of asking for them, -the `Car` constructor instantiates its own copies from -the very specific classes `Engine` and `Tires`. - -What if the `Engine` class evolves and its constructor requires a parameter? -That would break the `Car` class and it would stay broken until you rewrote it along the lines of -`this.engine = new Engine(theNewParameter)`. -The `Engine` constructor parameters weren't even a consideration when you first wrote `Car`. -You may not anticipate them even now. -But you'll *have* to start caring because -when the definition of `Engine` changes, the `Car` class must change. -That makes `Car` brittle. - -What if you want to put a different brand of tires on your `Car`? Too bad. -You're locked into whatever brand the `Tires` class creates. That makes the -`Car` class inflexible. - -Right now each new car gets its own `engine`. It can't share an `engine` with other cars. -While that makes sense for an automobile engine, -surely you can think of other dependencies that should be shared, such as the onboard -wireless connection to the manufacturer's service center. This `Car` lacks the flexibility -to share services that have been created previously for other consumers. - -When you write tests for `Car` you're at the mercy of its hidden dependencies. -Is it even possible to create a new `Engine` in a test environment? -What does `Engine` depend upon? What does that dependency depend on? -Will a new instance of `Engine` make an asynchronous call to the server? -You certainly don't want that going on during tests. - -What if the `Car` should flash a warning signal when tire pressure is low? -How do you confirm that it actually does flash a warning -if you can't swap in low-pressure tires during the test? - -You have no control over the car's hidden dependencies. -When you can't control the dependencies, a class becomes difficult to test. - -How can you make `Car` more robust, flexible, and testable? - -{@a ctor-injection} -That's super easy. Change the `Car` constructor to a version with DI: - - - - - - - - - - - -See what happened? The definition of the dependencies are -now in the constructor. -The `Car` class no longer creates an `engine` or `tires`. -It just consumes them. - -
- -This example leverages TypeScript's constructor syntax for declaring -parameters and properties simultaneously. - -
- -Now you can create a car by passing the engine and tires to the constructor. - - - - -How cool is that? -The definition of the `engine` and `tire` dependencies are -decoupled from the `Car` class. -You can pass in any kind of `engine` or `tires` you like, as long as they -conform to the general API requirements of an `engine` or `tires`. - -Now, if someone extends the `Engine` class, that is not `Car`'s problem. - -
- -The _consumer_ of `Car` has the problem. The consumer must update the car creation code to -something like this: - - - - - -The critical point is this: the `Car` class did not have to change. -You'll take care of the consumer's problem shortly. - -
- -The `Car` class is much easier to test now because you are in complete control -of its dependencies. -You can pass mocks to the constructor that do exactly what you want them to do -during each test: - - - - -**You just learned what dependency injection is**. - -It's a coding pattern in which a class receives its dependencies from external -sources rather than creating them itself. - -Cool! But what about that poor consumer? -Anyone who wants a `Car` must now -create all three parts: the `Car`, `Engine`, and `Tires`. -The `Car` class shed its problems at the consumer's expense. -You need something that takes care of assembling these parts. - -You _could_ write a giant class to do that: - - - - -It's not so bad now with only three creation methods. -But maintaining it will be hairy as the application grows. -This factory is going to become a huge spiderweb of -interdependent factory methods! - -Wouldn't it be nice if you could simply list the things you want to build without -having to define which dependency gets injected into what? - -This is where the dependency injection framework comes into play. -Imagine the framework had something called an _injector_. -You register some classes with this injector, and it figures out how to create them. - -When you need a `Car`, you simply ask the injector to get it for you and you're good to go. - - - - -Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`. -The consumer knows nothing about creating a `Car`. -You don't have a gigantic factory class to maintain. -Both `Car` and consumer simply ask for what they need and the injector delivers. - -This is what a **dependency injection framework** is all about. - -Now that you know what dependency injection is and appreciate its benefits, -turn to the [Angular Dependency Injection](guide/dependency-injection) guide to see how it is implemented in Angular. diff --git a/aio/content/guide/dependency-injection-providers.md b/aio/content/guide/dependency-injection-providers.md new file mode 100644 index 0000000000..b654d26e20 --- /dev/null +++ b/aio/content/guide/dependency-injection-providers.md @@ -0,0 +1,353 @@ +# Dependency Providers + +A dependency [provider](guide/glossary#provider) configures an injector +with a [DI token](guide/glossary#di-token), +which that injector uses to provide the concrete, runtime version of a dependency value. +The injector relies on the provider configuration to create instances of the dependencies +that it injects into components, directives, pipes, and other services. + +You must configure an injector with a provider, or it won't know how to create the dependency. +The most obvious way for an injector to create an instance of a service class is with the class itself. +If you specify the service class itself as the provider token, the default behavior is for the injector to instantiate that class with `new`. + +In the following typical example, the `Logger` class itself provides a `Logger` instance. + + + + +You can, however, configure an injector with an alternative provider, +in order to deliver some other object that provides the needed logging functionality. +For instance: +* You can provide a substitute class. + +* You can provide a logger-like object. + +* Your provider can call a logger factory function. + +{@a provide} + +## The `Provider` object literal + +The class-provider syntax is a shorthand expression that expands +into a provider configuration, defined by the [`Provider` interface](api/core/Provider). +The following code snippets shows how a class that is given as the `providers` value is expanded into a full provider object. + + + + + + + +The expanded provider configuration is an object literal with two properties. + +* The `provide` property holds the [token](guide/dependency-injection#token) +that serves as the key for both locating a dependency value and configuring the injector. + +* The second property is a provider definition object, which tells the injector how to create the dependency value. +The provider-definition key can be `useClass`, as in the example. +It can also be `useExisting`, `useValue`, or `useFactory`. +Each of these keys provides a different type of dependency, as discussed below. + + +{@a class-provider} + +## Alternative class providers + +Different classes can provide the same service. +For example, the following code tells the injector +to return a `BetterLogger` instance when the component asks for a logger +using the `Logger` token. + + + + +{@a class-provider-dependencies} + +### Class providers with dependencies + +Another class, `EvenBetterLogger`, might display the user name in the log message. +This logger gets the user from an injected `UserService` instance. + + + + +The injector needs providers for both this new logging service and its dependent `UserService`. Configure this alternative logger with the `useClass` provider-definition key, like `BetterLogger`. The following array specifies both providers in the `providers` metadata option of the parent module or component. + + + + +{@a aliased-class-providers} + +### Aliased class providers + +Suppose an old component depends upon the `OldLogger` class. +`OldLogger` has the same interface as `NewLogger`, but for some reason +you can't update the old component to use it. + +When the old component logs a message with `OldLogger`, +you want the singleton instance of `NewLogger` to handle it instead. +In this case, the dependency injector should inject that singleton instance +when a component asks for either the new or the old logger. +`OldLogger` should be an *alias* for `NewLogger`. + +If you try to alias `OldLogger` to `NewLogger` with `useClass`, you end up with two different `NewLogger` instances in your app. + + + + +To make sure there is only one instance of `NewLogger`, alias `OldLogger` with the `useExisting` option. + + + + +{@a value-provider} + +## Value providers + +Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class. +To inject an object you have already created, +configure the injector with the `useValue` option + +The following code defines a variable that creates such an object to play the logger role. + + + + +The following provider object uses the `useValue` key to associate the variable with the `Logger` token. + + + + +{@a non-class-dependencies} + +### Non-class dependencies + +Not all dependencies are classes. +Sometimes you want to inject a string, function, or object. + +Apps often define configuration objects with lots of small facts, +like the title of the application or the address of a web API endpoint. +These configuration objects aren't always instances of a class. +They can be object literals, as shown in the following example. + + + + +{@a interface-not-valid-token} + +**TypeScript interfaces are not valid tokens** + +The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface. +Unfortunately, you cannot use a TypeScript interface as a token. +In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation (token) that the DI framework can use. + + + + + + + +
+ +This might seem strange if you're used to dependency injection in strongly typed languages where an interface is the preferred dependency lookup key. +However, JavaScript, doesn't have interfaces, so when TypeScript is transpiled to JavaScript, the interface disappears. +There is no interface type information left for Angular to find at runtime. + +
+ +One alternative is to provide and inject the configuration object in an NgModule like `AppModule`. + + + +Another solution to choosing a provider token for non-class dependencies is +to define and use an `InjectionToken` object. +The following example shows how to define such a token. + + + + +The type parameter, while optional, conveys the dependency's type to developers and tooling. +The token description is another developer aid. + +Register the dependency provider using the `InjectionToken` object: + + + + +Now you can inject the configuration object into any constructor that needs it, with +the help of an `@Inject()` parameter decorator. + + + + +
+ +Although the `AppConfig` interface plays no role in dependency injection, +it supports typing of the configuration object within the class. + +
+ + +{@a factory-provider} +{@a factory-providers} + +## Factory providers + +Sometimes you need to create a dependent value dynamically, +based on information you won't have until run time. +For example, you might need information that changes repeatedly in the course of the browser session. +Also, your injectable service might not have independent access to the source of the information. + +In cases like this you can use a *factory provider*. +Factory providers can also be useful when creating an instance of a dependency from +a third-party library that wasn't designed to work with DI. + +For example, suppose `HeroService` must hide *secret* heroes from normal users. +Only authorized users should see secret heroes. + +Like `EvenBetterLogger`, `HeroService` needs to know if the user is authorized to see secret heroes. +That authorization can change during the course of a single application session, +as when you log in a different user. + +Let's say you don't want to inject `UserService` directly into `HeroService`, because you don't want to complicate that service with security-sensitive information. +`HeroService` won't have direct access to the user information to decide +who is authorized and who isn't. + +To resolve this, we give the `HeroService` constructor a boolean flag to control display of secret heroes. + + + + +You can inject `Logger`, but you can't inject the `isAuthorized` flag. Instead, you can use a factory provider to create a new logger instance for `HeroService`. + +A factory provider needs a factory function. + + + + +Although `HeroService` has no access to `UserService`, the factory function does. +You inject both `Logger` and `UserService` into the factory provider +and let the injector pass them along to the factory function. + + + + +* The `useFactory` field tells Angular that the provider is a factory function whose implementation is `heroServiceFactory`. + +* The `deps` property is an array of [provider tokens](guide/dependency-injection#token). +The `Logger` and `UserService` classes serve as tokens for their own class providers. +The injector resolves these tokens and injects the corresponding services into the matching factory function parameters. + +Notice that you captured the factory provider in an exported variable, `heroServiceProvider`. +This extra step makes the factory provider reusable. +You can configure a provider of `HeroService` with this variable wherever you need it. +In this sample, you need it only in `HeroesComponent`, +where `heroServiceProvider` replaces `HeroService` in the metadata `providers` array. + +The following shows the new and the old implementations side-by-side. + + + + + + + + + + + +## Predefined tokens and multiple providers + +Angular provides a number of built-in injection-token constants that you can use to customize the behavior of +various systems. + +For example, you can use the following built-in tokens as hooks into the framework’s bootstrapping and initialization process. +A provider object can associate any of these injection tokens with one or more callback functions that take app-specific initialization actions. + +* [PLATFORM_INITIALIZER](api/core/PLATFORM_INITIALIZER): Callback is invoked when a platform is initialized. + +* [APP_BOOTSTRAP_LISTENER](api/core/APP_BOOTSTRAP_LISTENER): Callback is invoked for each component that is bootstrapped. The handler function receives the ComponentRef instance of the bootstrapped component. + +* [APP_INITIALIZER](api/core/APP_INITIALIZER): Callback is invoked before an app is initialized. All registered initializers can optionally return a Promise. All initializer functions that return Promises must be resolved before the application is bootstrapped. If one of the initializers fails to resolves, the application is not bootstrapped. + +The provider object can have a third option, `multi: true`, which you can use with `APP_INITIALIZER` +to register multiple handlers for the provide event. + +For example, when bootstrapping an application, you can register many initializers using the same token. + +``` +export const APP_TOKENS = [ + { provide: PLATFORM_INITIALIZER, useFactory: platformInitialized, multi: true }, + { provide: APP_INITIALIZER, useFactory: delayBootstrapping, multi: true }, + { provide: APP_BOOTSTRAP_LISTENER, useFactory: appBootstrapped, multi: true }, +]; +``` + +Multiple providers can be associated with a single token in other areas as well. +For example, you can register a custom form validator using the built-in [NG_VALIDATORS](api/forms/NG_VALIDATORS) token, +and provide multiple instances of a given validator provider by using the `multi: true` property in the provider object. +Angular adds your custom validators to the existing collection. + +The Router also makes use of multiple providers associated with a single token. +When you provide multiple sets of routes using [RouterModule.forRoot](api/router/RouterModule#forroot) +and [RouterModule.forChild](api/router/RouterModule#forchild) in a single module, +the [ROUTES](api/router/ROUTES) token combines all the different provided sets of routes into a single value. + +
+ +Ideally, if an application isn't injecting a service, it shouldn't be included in the final output. +However, Angular has to be able to identify at build time whether the service will be required or not. +Because it's always possible to inject a service directly using `injector.get(Service)`, +Angular can't identify all of the places in your code where this injection could happen, +so it has no choice but to include the service in the injector. +Thus, services provided at the NgModule or component level are not tree-shakable. + +
+ +The following example of non-tree-shakable providers in Angular configures a service provider for the injector of an NgModule. + + + +This module can then be imported into your application module +to make the service available for injection in your app, +as shown in the following example. + + + +When `ngc` runs, it compiles `AppModule` into a module factory, which contains definitions for all the providers declared in all the modules it includes. At runtime, this factory becomes an injector that instantiates these services. + +Tree-shaking doesn't work here because Angular can't decide to exclude one chunk of code (the provider definition for the service within the module factory) based on whether another chunk of code (the service class) is used. To make services tree-shakable, the information about how to construct an instance of the service (the provider definition) needs to be a part of the service class itself. + +### Creating tree-shakable providers + +You can make a provider tree-shakable by specifying it in the `@Injectable()` decorator on the service itself, rather than in the metadata for the NgModule or component that depends on the service. + +The following example shows the tree-shakable equivalent to the `ServiceModule` example above. + + + +The service can be instantiated by configuring a factory function, as in the following example. + + + +
+ +To override a tree-shakable provider, configure the injector of a specific NgModule or component with another provider, using the `providers: []` array syntax of the `@NgModule()` or `@Component()` decorator. + +
\ No newline at end of file diff --git a/aio/content/guide/dependency-injection.md b/aio/content/guide/dependency-injection.md index 673e4da756..860e66f26c 100644 --- a/aio/content/guide/dependency-injection.md +++ b/aio/content/guide/dependency-injection.md @@ -1,23 +1,25 @@ -# Angular Dependency Injection +# Dependency Injection in Angular -**Dependency Injection (DI)** is a way to create objects that depend upon other objects. -A Dependency Injection system supplies the dependent objects (called the _dependencies_) -when it creates an instance of an object. +Dependency injection (DI), is an important application design pattern. +Angular has its own DI framework, which is typically +used in the design of Angular applications to increase their efficiency and modularity. -The [Dependency Injection pattern](guide/dependency-injection-pattern) page describes this general approach. -_The guide you're reading now_ explains how Angular's own Dependency Injection system works. +Dependencies are services or objects that a class needs to perform its function. +DI is a coding pattern in which a class asks for dependencies from external sources rather than creating them itself. -## DI by example +In Angular, the DI framework provides declared dependencies to a class when that class is instantiated. This guide explains how DI works in Angular, and how you use it to make your apps flexible, efficient, and robust, as well as testable and maintainable. -You'll learn Angular Dependency Injection through a discussion of the sample app that accompanies this guide. -Run the anytime. +
+ + You can run the of the sample app that accompanies this guide. + +
Start by reviewing this simplified version of the _heroes_ feature -from the [The Tour of Heroes](tutorial/). +from the [The Tour of Heroes](tutorial/). This simple version doesn't use DI; we'll walk through converting it to do so. - + @@ -31,26 +33,40 @@ from the [The Tour of Heroes](tutorial/). -The `HeroesComponent` is the top-level heroes component. -Its only purpose is to display the `HeroListComponent` -which displays a list of hero names. +`HeroesComponent` is the top-level heroes component. +Its only purpose is to display `HeroListComponent`, which displays a list of hero names. -This version of the `HeroListComponent` gets its `heroes` from the `HEROES` array, an in-memory collection +This version of the `HeroListComponent` gets heroes from the `HEROES` array, an in-memory collection defined in a separate `mock-heroes` file. - + -That may suffice in the early stages of development, but it's far from ideal. +This approach works for prototyping, but is not robust or maintainable. As soon as you try to test this component or get heroes from a remote server, -you'll have to change the implementation of `HerosListComponent` and -replace every other use of the `HEROES` mock data. +you have to change the implementation of `HeroesListComponent` and +replace every use of the `HEROES` mock data. -It's better to hide these details inside a _service_ class, -[defined in its own file](#one-class-per-file). -## Create an injectable _HeroService_ +## Create and register an injectable service + +The DI framework lets you supply data to a component from an injectable _service_ class, defined in its own file. To demonstrate, we'll create an injectable service class that provides a list of heroes, and register that class as a provider of that service. + +
+ +Having multiple classes in the same file can be confusing. We generally recommend that you define components and services in separate files. + +If you do combine a component and service in the same file, +it is important to define the service first, and then the component. If you define the component before the service, you get a run-time null reference error. + +It is possible to define the component first with the help of the `forwardRef()` method as explained in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html). + +You can also use forward references to break circular dependencies. +See an example in the [DI Cookbook](guide/dependency-injection-in-action#forwardref). + +
+ +### Create an injectable service class The [**Angular CLI**](https://cli.angular.io/) can generate a new `HeroService` class in the `src/app/heroes` folder with this command. @@ -58,412 +74,84 @@ The [**Angular CLI**](https://cli.angular.io/) can generate a new `HeroService` ng generate service heroes/hero
-The command above creates the following `HeroService` skeleton. +The command creates the following `HeroService` skeleton. -The `@Injectable` decorator is an essential ingredient in every Angular service definition. -The rest of the class has been rewritten to expose a `getHeroes` method -that returns the same mock data as before. +The `@Injectable()` is an essential ingredient in every Angular service definition. The rest of the class has been written to expose a `getHeroes` method that returns the same mock data as before. (A real app would probably get its data asynchronously from a remote server, but we'll ignore that to focus on the mechanics of injecting the service.) - + -Of course, this isn't a real data service. -If the app were actually getting data from a remote server, -the `getHeroes` method signature would have to be asynchronous. - -That's a defect we can safely ignore in this guide where our focus is on -_injecting the service_ into the `HeroList` component. - {@a injector-config} {@a bootstrap} -## Injectors +### Configure an injector with a service provider -A _service_ like `HeroService` is just a class in Angular until you register it with an Angular dependency injector. +The class we have created provides a service. The `@Injectable()` decorator marks it as a service +that can be injected, but Angular can't actually inject it anywhere until you configure +an Angular [dependency injector](guide/glossary#injector) with a [provider](guide/glossary#provider) of that service. -An Angular injector is responsible for creating service instances and injecting them into classes like the `HeroListComponent`. +The injector is responsible for creating service instances and injecting them into classes like `HeroListComponent`. +You rarely create an Angular injector yourself. Angular creates injectors for you as it executes the app, starting with the _root injector_ that it creates during the [bootstrap process](guide/bootstrapping). -You rarely create an Angular injector yourself. -Angular creates injectors for you as it executes the app, -starting with the _root injector_ that it creates during the [bootstrap process](guide/bootstrapping). +A provider tells an injector _how to create the service_. +You must configure an injector with a provider before that injector can create a service (or provide any other kind of dependency). -Angular doesn't automatically know how you want to create instances of your services or the injector to create your service. You must configure it by specifying providers for every service. - -**Providers** tell the injector _how to create the service_. -Without a provider, the injector would not know -that it is responsible for injecting the service -nor be able to create the service. +A provider can be the service class itself, so that the injector can use `new` to create an instance. +You might also define more than one class to provide the same service in different ways, +and configure different injectors with different providers.
-You'll learn much more about _providers_ [below](#providers). -For now, it is sufficient to know that they configure where and how services are created. +Injectors are inherited, which means that if a given injector can't resolve a dependency, +it asks the parent injector to resolve it. +A component can get services from its own injector, +from the injectors of its component ancestors, +from the injector of its parent NgModule, or from the `root` injector. + +* Learn more about the [different kinds of providers](guide/dependency-injection-providers). + +* Learn more about how the [injector hierarchy](guide/hierarchical-dependency-injection) works.
-There are many ways to register a service provider with an injector. This section shows the most common ways -of configuring a provider for your services. +You can configure injectors with providers at different levels of your app, by setting a metadata value in one of three places: -{@a register-providers-injectable} +* In the `@Injectable()` decorator for the service itself. -## @Injectable providers +* In the `@NgModule()` decorator for an NgModule. -The `@Injectable` decorator identifies services and other classes that are intended to be injected. It can also be used to configure a provider for those services. +* In the `@Component()` decorator for a component. -Here we configure a provider for `HeroService` using the `@Injectable` decorator on the class. +The `@Injectable()` decorator has the `providedIn` metadata option, where you can specify the provider of the decorated service class with the `root` injector, or with the injector for a specific NgModule. - - -`providedIn` tells Angular that the root injector is responsible for creating an instance of the `HeroService` (by invoking its constructor) and making it available across the application. The CLI sets up this kind of a provider automatically for you when generating a new service. - -Sometimes it's not desirable to have a service always be provided in the application root injector. Perhaps users should explicitly opt-in to using the service, or the service should be provided in a lazily-loaded context. In this case, the provider should be associated with a specific `@NgModule` class, and will be used by whichever injector includes that module. - -In the following excerpt, the `@Injectable` decorator is used to configure a provider that will be available in any injector that includes the HeroModule. - - - -{@a register-providers-ngmodule} - -### _@NgModule_ providers - -In the following excerpt, the root `AppModule` registers two providers in its `providers` array. - - - - -The first entry registers the `UserService` class (_not shown_) under the `UserService` _injection token_. -The second registers a value (`HERO_DI_CONFIG`) under the `APP_CONFIG` _injection token_. - -With the above registrations, Angular can inject the `UserService` or the `HERO_DI_CONFIG` value -into any class that it creates. +The `@NgModule()` and `@Component()` decorators have the `providers` metadata option, where you can configure providers for NgModule-level or component-level injectors.
-You'll learn about _injection tokens_ and _provider_ syntax [below](#providers). -
+Components are directives, and the `providers` option is inherited from `@Directive()`. You can also configure providers for directives and pipes at the same level as the component. -{@a register-providers-component} - -### _@Component_ providers - -In addition to providing the service application-wide or within a particular `@NgModule`, services can also be provided in specific components. Services provided in component-level is only available within that component injector or in any of its child components. - -The example below shows a revised `HeroesComponent` that registers the `HeroService` in its `providers` array. - - - - -{@a ngmodule-vs-comp} - -### @Injectable, _@NgModule_ or _@Component_? - -Should you provide a service with an `@Injectable` decorator, in an `@NgModule`, or within an `@Component`? -The choices lead to differences in the final bundle size, service _scope_, and service _lifetime_. - -When you register providers in the **@Injectable** decorator of the service itself, optimization tools such as those used by the CLI's production builds can perform tree shaking, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes. - -**Angular module providers** (`@NgModule.providers`) are registered with the application's root injector. -Angular can inject the corresponding services in any class it creates. -Once created, a service instance lives for the life of the app and Angular injects this one service instance in every class that needs it. - -You're likely to inject the `UserService` in many places throughout the app -and will want to inject the same service instance every time. -Providing the `UserService` with an Angular module is a good choice if an `@Injectable` provider is not an option.. - -
- -To be precise, Angular module providers are registered with the root injector -_unless the module is_ [lazy loaded](guide/lazy-loading-ngmodules). -In this sample, all modules are _eagerly loaded_ when the application starts, -so all module providers are registered with the app's root injector. - -

- -
- -**A component's providers** (`@Component.providers`) are registered with each component instance's own injector. - -Angular can only inject the corresponding services in that component instance or one of its descendant component instances. -Angular cannot inject the same service instance anywhere else. - -Note that a component-provided service may have a limited lifetime. Each new instance of the component gets its own instance of the service -and, when the component instance is destroyed, so is that service instance. - -In this sample app, the `HeroComponent` is created when the application starts -and is never destroyed so the `HeroService` created for the `HeroComponent` also live for the life of the app. - -If you want to restrict `HeroService` access to the `HeroComponent` and its nested `HeroListComponent`, -providing the `HeroService` in the `HeroComponent` may be a good choice. - -
- -The scope and lifetime of component-provided services is a consequence of [the way Angular creates component instances](#component-child-injectors). - -
- - -{@a providers} - -## Providers - -A service provider *provides* the concrete, runtime version of a dependency value. -The injector relies on **providers** to create instances of the services -that the injector injects into components, directives, pipes, and other services. - -You must register a service *provider* with an injector, or it won't know how to create the service. - -The next few sections explain the many ways you can specify a provider. - -### The class as its own provider - -There are many ways to *provide* something that looks and behaves like a `Logger`. -The `Logger` class itself is an obvious and natural provider. - - - - -But it's not the only way. - -You can configure the injector with alternative providers that can deliver an object that behaves like a `Logger`. -You could provide a substitute class. You could provide a logger-like object. -You could give it a provider that calls a logger factory function. -Any of these approaches might be a good choice under the right circumstances. - -What matters is that the injector has a provider to go to when it needs a `Logger`. - -{@a provide} - -### The _provide_ object literal - -Here's the class-provider syntax again. - - - - -This is actually a shorthand expression for a provider registration -using a _provider_ object literal with two properties: - - - - -The `provide` property holds the [token](guide/dependency-injection#token) that serves as the key for both locating a dependency value -and registering the provider. - -The second property is always a provider definition object, -which you can think of as a *recipe* for creating the dependency value. -There are many ways to create dependency values just as there are many ways to write a recipe. - -{@a class-provider} - -### Alternative class providers - -Occasionally you'll ask a different class to provide the service. -The following code tells the injector -to return a `BetterLogger` when something asks for the `Logger`. - - - - -{@a class-provider-dependencies} - -### Class provider with dependencies - -Maybe an `EvenBetterLogger` could display the user name in the log message. -This logger gets the user from the injected `UserService`, -which is also injected at the application level. - - - - -Configure it like `BetterLogger`. - - - - -{@a aliased-class-providers} - -### Aliased class providers - -Suppose an old component depends upon an `OldLogger` class. -`OldLogger` has the same interface as the `NewLogger`, but for some reason -you can't update the old component to use it. - -When the *old* component logs a message with `OldLogger`, -you'd like the singleton instance of `NewLogger` to handle it instead. - -The dependency injector should inject that singleton instance -when a component asks for either the new or the old logger. -The `OldLogger` should be an alias for `NewLogger`. - -You certainly do not want two different `NewLogger` instances in your app. -Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger` with `useClass`. - - - - -The solution: alias with the `useExisting` option. - - - - -{@a value-provider} - -### Value providers - -Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class. - - - - -Then you register a provider with the `useValue` option, -which makes this object play the logger role. - - - - -See more `useValue` examples in the -[Non-class dependencies](guide/dependency-injection#non-class-dependencies) and -[InjectionToken](guide/dependency-injection#injection-token) sections. - -{@a factory-provider} - -### Factory providers - -Sometimes you need to create the dependent value dynamically, -based on information you won't have until the last possible moment. -Maybe the information changes repeatedly in the course of the browser session. - -Suppose also that the injectable service has no independent access to the source of this information. - -This situation calls for a **factory provider**. - -To illustrate the point, add a new business requirement: -the `HeroService` must hide *secret* heroes from normal users. -Only authorized users should see secret heroes. - -Like the `EvenBetterLogger`, the `HeroService` needs a fact about the user. -It needs to know if the user is authorized to see secret heroes. -That authorization can change during the course of a single application session, -as when you log in a different user. - -Unlike `EvenBetterLogger`, you can't inject the `UserService` into the `HeroService`. -The `HeroService` won't have direct access to the user information to decide -who is authorized and who is not. - -Instead, the `HeroService` constructor takes a boolean flag to control display of secret heroes. - - - - -You can inject the `Logger`, but you can't inject the boolean `isAuthorized`. -You'll have to take over the creation of new instances of this `HeroService` with a factory provider. - -A factory provider needs a factory function: - - - - -Although the `HeroService` has no access to the `UserService`, the factory function does. - -You inject both the `Logger` and the `UserService` into the factory provider -and let the injector pass them along to the factory function: - - - - -
- -The `useFactory` field tells Angular that the provider is a factory function -whose implementation is the `heroServiceFactory`. - -The `deps` property is an array of [provider tokens](guide/dependency-injection#token). -The `Logger` and `UserService` classes serve as tokens for their own class providers. -The injector resolves these tokens and injects the corresponding services into the matching factory function parameters. - -
- -Notice that you captured the factory provider in an exported variable, `heroServiceProvider`. -This extra step makes the factory provider reusable. -You can register the `HeroService` with this variable wherever you need it. - -In this sample, you need it only in the `HeroesComponent`, -where it replaces the previous `HeroService` registration in the metadata `providers` array. -Here you see the new and the old implementation side-by-side: - - - - - - - - - - - -{@a tree-shakable-provider} - -### Tree-shakable providers - -Tree shaking is the ability to remove code that is not referenced in an application from the final bundle. Tree-shakable providers give Angular the ability to remove services that are not used in your application from the final output. This significantly reduces the size of your bundles. - -Ideally, if an application is not injecting a service, it should not be included in the final output. However, it turns out that the Angular compiler cannot identify at build time if the service will be required or not. Because it's always possible to inject a service directly using `injector.get(Service)`, Angular cannot identify all of the places in your code where this injection could happen, so it has no choice but to include the service in the injector regardless. Thus, services provided in modules are not tree-shakable. - -Let us consider an example of non-tree-shakable providers in Angular. - -In this example, to provide services in Angular, you include them in an `@NgModule`: - - - -This module can then be imported into your application module, to make the service available for injection in your app: - - - -When `ngc` runs, it compiles AppModule into a module factory, which contains definitions for all the providers declared in all the modules it includes. At runtime, this factory becomes an injector that instantiates these services. - -Tree-shaking doesn't work in the method above because Angular cannot decide to exclude one chunk of code (the provider definition for the service within the module factory) based on whether another chunk of code (the service class) is used. To make services tree-shakable, the information about how to construct an instance of the service (the provider definition) needs to be a part of the service class itself. - -#### Creating tree-shakable providers - -To create providers that are tree-shakable, the information that used to be specified in the module should be specified in the `@Injectable` decorator on the service itself. - -The following example shows the tree-shakable equivalent to the `ServiceModule` example above: - - - -In the example above, `providedIn` allows you to declare the injector which injects this service. Unless there is a special case, the value should always be root. Setting the value to root ensures that the service is scoped to the root injector, without naming a particular module that is present in that injector. - -The service can be instantiated by configuring a factory function as shown below: - - - -
- -To override tree-shakable providers, register the provider using the `providers: []` array syntax of any Angular decorator that supports it. +Learn more about [where to configure providers](guide/hierarchical-dependency-injection#where-to-register).
{@a injector-config} {@a bootstrap} -## Inject a service +## Injecting services -The `HeroListComponent` should get heroes from the `HeroService`. +In order for `HeroListComponent` to get heroes from `HeroService`, it needs to ask for `HeroService` to be injected, rather than creating it's own `HeroService` instance with `new`. -The component shouldn't create the `HeroService` with `new`. -It should ask for the `HeroService` to be injected. - -You can tell Angular to inject a dependency in the component's constructor by specifying a **constructor parameter with the dependency type**. -Here's the `HeroListComponent` constructor, asking for the `HeroService` to be injected. +You can tell Angular to inject a dependency in a component's constructor by specifying a **constructor parameter with the dependency type**. Here's the `HeroListComponent` constructor, asking for the `HeroService` to be injected. -Of course, the `HeroListComponent` should do something with the injected `HeroService`. +Of course, `HeroListComponent` should do something with the injected `HeroService`. Here's the revised component, making use of the injected service, side-by-side with the previous version for comparison. @@ -474,60 +162,40 @@ Here's the revised component, making use of the injected service, side-by-side w -Notice that the `HeroListComponent` doesn't know where the `HeroService` comes from. -_You_ know that it comes from the parent `HeroesComponent`. -If you decided instead to provide the `HeroService` in the `AppModule`, -the `HeroListComponent` wouldn't change at all. -The _only thing that matters_ is that the `HeroService` is provided in some parent injector. +`HeroService` must provided in some parent injector. The code in `HeroListComponent` doesn't depend on where `HeroService` comes from. +If you decided to provide `HeroService` in `AppModule`, `HeroListComponent` wouldn't change. {@a singleton-services} - -## Singleton services - -Services are singletons _within the scope of an injector_. -There is at most one instance of a service in a given injector. - -There is only one root injector, and the `UserService` is registered with that injector. -Therefore, there can be just one `UserService` instance in the entire app, -and every class that injects `UserService` get this service instance. - -However, Angular DI is a -[hierarchical injection system](guide/hierarchical-dependency-injection), -which means that nested injectors can create their own service instances. -Angular creates nested injectors all the time. - {@a component-child-injectors} -## Component child injectors +### Injector hierarchy and service instances -Component injectors are independent of each other and -each of them creates its own instances of the component-provided services. +Services are singletons _within the scope of an injector_. That is, there is at most one instance of a service in a given injector. -For example, when Angular creates a new instance of a component that has `@Component.providers`, -it also creates a new _child injector_ for that instance. +There is only one root injector for an app. Providing `UserService` at the `root` or `AppModule` level means it is registered with the root injector. There is just one `UserService` instance in the entire app and every class that injects `UserService` gets this service instance _unless_ you configure another provider with a _child injector_. -When Angular destroys one of these component instances, it also destroys the -component's injector and that injector's service instances. +Angular DI has a [hierarchical injection system](guide/hierarchical-dependency-injection), which means that nested injectors can create their own service instances. +Angular regularly creates nested injectors. Whenever Angular creates a new instance of a component that has `providers` specified in `@Component()`, it also creates a new _child injector_ for that instance. +Similarly, when a new NgModule is lazy-loaded at run time, Angular can create an injector for it with its own providers. -Because of [injector inheritance](guide/hierarchical-dependency-injection), +Child modules and component injectors are independent of each other, and create their own separate instances of the provided services. When Angular destroys an NgModule or component instance, it also destroys that injector and that injector's service instances. + +Thanks to [injector inheritance](guide/hierarchical-dependency-injection), you can still inject application-wide services into these components. A component's injector is a child of its parent component's injector, -and a descendent of its parent's parent's injector, and so on all the way back to the application's _root_ injector. -Angular can inject a service provided by any injector in that lineage. +and a descendent of its parent's parent's injector, and so on all the way back to the application's _root_ injector. Angular can inject a service provided by any injector in that lineage. -For example, Angular could inject a `HeroListComponent` -with both the `HeroService` provided in `HeroComponent` -and the `UserService` provided in `AppModule`. +For example, Angular can inject `HeroListComponent` with both the `HeroService` provided in `HeroComponent` and the `UserService` provided in `AppModule`. {@a testing-the-component} -## Testing the component +## Testing components with dependencies -Earlier you saw that designing a class for dependency injection makes the class easier to test. +Designing a class with dependency injection makes the class easier to test. Listing dependencies as constructor parameters may be all you need to test application parts effectively. For example, you can create a new `HeroListComponent` with a mock service that you can manipulate -under test: +under test. @@ -540,15 +208,12 @@ Learn more in the [Testing](guide/testing) guide. {@a service-needs-service} -## When the service needs a service +## Services that need other services -The `HeroService` is very simple. It doesn't have any dependencies of its own. - -What if it had a dependency? What if it reported its activities through a logging service? -You'd apply the same *constructor injection* pattern, +Service can have their own dependencies. `HeroService` is very simple and doesn't have any dependencies of its own. Suppose, however, that you want it to report its activities through a logging service. You can apply the same *constructor injection* pattern, adding a constructor that takes a `Logger` parameter. -Here is the revised `HeroService` that injects the `Logger`, side-by-side with the previous service for comparison. +Here is the revised `HeroService` that injects `Logger`, side by side with the previous service for comparison. @@ -558,154 +223,85 @@ Here is the revised `HeroService` that injects the `Logger`, side-by-side with t + + + -The constructor asks for an injected instance of a `Logger` and stores it in a private field called `logger`. -The `getHeroes()` method logs a message when asked to fetch heroes. +The constructor asks for an injected instance of `Logger` and stores it in a private field called `logger`. The `getHeroes()` method logs a message when asked to fetch heroes. +Notice that the `Logger` service also has the `@Injectable()` decorator, even though it might not need its own dependencies. In fact, the `@Injectable()` decorator is **required for all services**. -{@a logger-service} +When Angular creates a class whose constructor has parameters, it looks for type and injection metadata about those parameters so that it can inject the correct service. +If Angular can't find that parameter information, it throws an error. +Angular can only find the parameter information _if the class has a decorator of some kind_. +The `@Injectable()` decorator is the standard decorator for service classes. -#### The dependent _Logger_ service - -The sample app's `Logger` service is quite simple: - - - - -If the app didn't provide this `Logger`, -Angular would throw an exception when it looked for a `Logger` to inject -into the `HeroService`. - - - ERROR Error: No provider for Logger! - - -Because a singleton logger service is useful everywhere, -it's provided in the root `AppModule`. - - - +
+ The decorator requirement is imposed by TypeScript. TypeScript normally discards parameter type information when it [transpiles]((guide/glossary#transpile) the code to JavaScript. TypeScript preserves this information if the class has a decorator and the `emitDecoratorMetadata` compiler option is set `true` in TypeScript's `tsconfig.json` configuration file. The CLI configures `tsconfig.json` with `emitDecoratorMetadata: true`. + + This means you're responsible for putting `@Injectable()` on your service classes. +
{@a token} -## Dependency injection tokens +{@a injection-token} -When you register a provider with an injector, you associate that provider with a dependency injection token. +### Dependency injection tokens + +When you configure an injector with a provider, you associate that provider with a [DI token](guide/glossary#di-token). The injector maintains an internal *token-provider* map that it references when asked for a dependency. The token is the key to the map. -In all previous examples, the dependency value has been a class *instance*, and -the class *type* served as its own lookup key. +In simple examples, the dependency value is an *instance*, and +the class *type* serves as its own lookup key. Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token: -You have similar good fortune when you write a constructor that requires an injected class-based dependency. +The behavior is similar when you write a constructor that requires an injected class-based dependency. When you define a constructor parameter with the `HeroService` class type, -Angular knows to inject the -service associated with that `HeroService` class token: +Angular knows to inject the service associated with that `HeroService` class token: -This is especially convenient when you consider that most dependency values are provided by classes. +Many dependency values are provided by classes, but not all. The expanded *provide* object lets you associate different kinds of providers with a DI token. -{@a non-class-dependencies} - -### Non-class dependencies - -What if the dependency value isn't a class? Sometimes the thing you want to inject is a -string, function, or object. - -Applications often define configuration objects with lots of small facts -(like the title of the application or the address of a web API endpoint) -but these configuration objects aren't always instances of a class. -They can be object literals such as this one: - - - - -What if you'd like to make this configuration object available for injection? -You know you can register an object with a [value provider](guide/dependency-injection#value-provider). - -But what should you use as the token? -You don't have a class to serve as a token. -There is no `AppConfig` class. - -
- -### TypeScript interfaces aren't valid tokens - -The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface. -Unfortunately, you cannot use a TypeScript interface as a token: - - - - - - - -That seems strange if you're used to dependency injection in strongly typed languages, where -an interface is the preferred dependency lookup key. - -It's not Angular's doing. An interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces. -The TypeScript interface disappears from the generated JavaScript. -There is no interface type information left for Angular to find at runtime. - -
- - -{@a injection-token} - -### _InjectionToken_ - -One solution to choosing a provider token for non-class dependencies is -to define and use an [*InjectionToken*](api/core/InjectionToken). -The definition of such a token looks like this: - - -import { InjectionToken } from '@angular/core'; -export const TOKEN = new InjectionToken('desc'); - - -You can directly configure a provider when creating an `InjectionToken`. The provider configuration determines which injector provides the token and how the value will be created. This is similar to using `@Injectable`, except that you cannot define standard providers (such as `useClass` or `useFactory`) with `InjectionToken`. Instead, you specify a factory function which returns the value to be provided directly. - - -export const TOKEN = - new InjectionToken('desc', { providedIn: 'root', factory: () => new AppConfig(), }) - - -Now you can inject the configuration object into any constructor that needs it, with -the help of an `@Inject` decorator: - - -constructor(@Inject(TOKEN)); - - -If the factory function needs access to other DI tokens, it can use the inject function from `@angular/core` to request dependencies. - - -const TOKEN = - new InjectionToken('tree-shakable token', - { providedIn: 'root', factory: () => - new AppConfig(inject(Parameter1), inject(Parameter2)), }); - +* Learn more about [different kinds of providers](guide/dependency-injection-providers). {@a optional} -## Optional dependencies +### Optional dependencies -You can tell Angular that the dependency is optional by annotating the constructor argument with null: +`HeroService` *requires* a logger, but what if it could get by without +one? - -constructor(@Inject(Token, null)); +When a component or service declares a dependency, the class constructor takes that dependency as a parameter. +You can tell Angular that the dependency is optional by annotating the +constructor parameter with `@Optional()`. + + -When using optional dependencies, your code must be prepared for a null value. + + + +When using `@Optional()`, your code must be prepared for a null value. If you +don't register a logger provider anywhere, the injector sets the +value of `logger` to null. + +
+ +`@Inject()` and `@Optional()` are _parameter decorators_. They alter the way the DI framework provides a dependency, by annotating the dependency parameter on the constructor of the class that requires the dependency. + +Learn more about parameter decorators in [Hierarchical Dependency Injectors](guide/hierarchical-dependency-injection). + +
## Summary @@ -714,74 +310,13 @@ You can register various kinds of providers, and you know how to ask for an injected object (such as a service) by adding a parameter to a constructor. -Angular dependency injection is more capable than this guide has described. -You can learn more about its advanced features, beginning with its support for -nested injectors, in +Dive deeper into the capabilities and advanced feature of the Angular DI system in the following pages: + +* Learn more about nested injectors in [Hierarchical Dependency Injection](guide/hierarchical-dependency-injection). -{@a explicit-injector} +* Learn more about [DI tokens and providers](guide/dependency-injection-providers). -## Appendix: Working with injectors directly - -Developers rarely work directly with an injector, but -here's an `InjectorComponent` that does. - - - - -An `Injector` is itself an injectable service. - -In this example, Angular injects the component's own `Injector` into the component's constructor. -The component then asks the injected injector for the services it wants in `ngOnInit()`. - -Note that the services themselves are not injected into the component. -They are retrieved by calling `injector.get()`. - -The `get()` method throws an error if it can't resolve the requested service. -You can call `get()` with a second parameter, which is the value to return if the service -is not found. Angular can't find the service if it's not registered with this or any ancestor injector. +* [Dependency Injection in Action](guide/dependency-injection-in-action) is a cookbook for some of the interesting things you can do with DI. -
- - - -The technique is an example of the -[service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern). - -**Avoid** this technique unless you genuinely need it. -It encourages a careless grab-bag approach such as you see here. -It's difficult to explain, understand, and test. -You can't know by inspecting the constructor what this class requires or what it will do. -It could acquire services from any ancestor component, not just its own. -You're forced to spelunk the implementation to discover what it does. - -Framework developers may take this approach when they -must acquire services generically and dynamically. - - -
- -{@a one-class-per-file} - -## Appendix: one class per file - -Having multiple classes in the same file is confusing and best avoided. -Developers expect one class per file. Keep them happy. - -If you combine the `HeroService` class with -the `HeroesComponent` in the same file, -**define the component last**. -If you define the component before the service, -you'll get a runtime null reference error. - - -
- -You actually can define the component first with the help of the `forwardRef()` method as explained -in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html). - -But it's best to avoid the problem altogether by defining components and services in separate files. - -
- diff --git a/aio/content/guide/hierarchical-dependency-injection.md b/aio/content/guide/hierarchical-dependency-injection.md index 48b671b8bd..84ab3b38de 100644 --- a/aio/content/guide/hierarchical-dependency-injection.md +++ b/aio/content/guide/hierarchical-dependency-injection.md @@ -1,120 +1,216 @@ # Hierarchical Dependency Injectors -You learned the basics of Angular Dependency injection in the -[Dependency Injection](guide/dependency-injection) guide. - -Angular has a _Hierarchical Dependency Injection_ system. -There is actually a tree of injectors that parallel an application's component tree. +The Angular dependency injection system is _hierarchical_. +There is a tree of injectors that parallel an app's component tree. You can reconfigure the injectors at any level of that component tree. This guide explores this system and how to use it to your advantage. +It uses examples based on this . -Try the . +{@a ngmodule-vs-comp} +{@a where-to-register} +## Where to configure providers +You can configure providers for different injectors in the injector hierarchy. +An internal platform-level injector is shared by all running apps. +The `AppModule` injector is the root of an app-wide injector hierarchy, and within +an NgModule, directive-level injectors follow the structure of the component hierarchy. -## The injector tree +The choices you make about where to configure providers lead to differences in the final bundle size, service _scope_, and service _lifetime_. -In the [Dependency Injection](guide/dependency-injection) guide, -you learned how to configure a dependency injector and how to retrieve dependencies where you need them. +When you specify providers in the `@Injectable()` decorator of the service itself (typically at the app root level), optimization tools such as those used by the CLI's production builds can perform *tree shaking*, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes. -In fact, there is no such thing as ***the*** injector. -An application may have multiple injectors. -An Angular application is a tree of components. Each component instance has its own injector. -The tree of components parallels the tree of injectors. +* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers). +You're likely to inject `UserService` in many places throughout the app and will want to inject the same service instance every time. Providing `UserService` through the `root` injector is a good choice, and is the default that the CLI uses when you generate a service for your app. -
+
+
Platform injector
+When you use `providedIn:'root'`, you are configuring the root injector for the _app_, which is the injector for `AppModule`. +The actual root of the entire injector hierarchy is a _platform injector_ that is the parent of app-root injectors. +This allows multiple apps to share a platform configuration. For example, a browser has only one URL bar, no matter how many apps you have running. +The platform injector is used internally during bootstrap, to configure platform-specific dependencies. You can configure additional platform-specific providers at the platform level by supplying `extraProviders` using the `platformBrowser()` function. -The component's injector may be a _proxy_ for an ancestor injector higher in the component tree. -That's an implementation detail that improves efficiency. -You won't notice the difference and -your mental model should be that every component has its own injector. +Learn more about dependency resolution through the injector hierarchy: +[What you always wanted to know about Angular Dependency Injection tree](https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d)
+*NgModule-level* providers can be specified with `@NgModule()` `providers` metadata option, or in the `@Injectable()` `providedIn` option (with some module other than the root `AppModule`). +Use the `@NgModule()` `provides` option if a module is [lazy loaded](guide/lazy-loading-ngmodules). The module's own injector is configured with the provider when that module is loaded, and Angular can inject the corresponding services in any class it creates in that module. If you use the `@Injectable()` option `providedIn: MyLazyloadModule`, the provider could be shaken out at compile time, if it is not used anywhere else in the app. + +* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers). + +For both root-level and module-level injectors, a service instance lives for the life of the app or module, and Angular injects this one service instance in every class that needs it. + +*Component-level* providers configure each component instance's own injector. +Angular can only inject the corresponding services in that component instance or one of its descendant component instances. +Angular can't inject the same service instance anywhere else. + +A component-provided service may have a limited lifetime. +Each new instance of the component gets its own instance of the service. +When the component instance is destroyed, so is that service instance. + +In our sample app, `HeroComponent` is created when the application starts +and is never destroyed, +so the `HeroService` instance created for `HeroComponent` lives for the life of the app. +If you want to restrict `HeroService` access to `HeroComponent` and its nested +`HeroListComponent`, provide `HeroService` at the component level, in `HeroComponent` metadata. + +* See more [examples of component-level injection](#component-injectors) below. + + +{@a register-providers-injectable} + +### @Injectable-level configuration + +The `@Injectable()` decorator identifies every service class. The `providedIn` metadata option for a service class configures a specific injector (typically `root`) +to use the decorated class as a provider of the service. +When an injectable class provides its own service to the `root` injector, the service is available anywhere the class is imported. + +The following example configures a provider for `HeroService` using the `@Injectable()` decorator on the class. + + + +This configuration tells Angular that the app's root injector is responsible for creating an +instance of `HeroService` by invoking its constructor, +and for making that instance available across the application. + +Providing a service with the app's root injector is a typical case, +and the CLI sets up this kind of a provider automatically for you +when generating a new service. +However, you might not always want to provide your service at the root level. +You might, for instance, want users to explicitly opt-in to using the service. + +Instead of specifying the `root` injector, you can set `providedIn` to a specific NgModule. + +For example, in the following excerpt, the `@Injectable()` decorator configures a provider +that is available in any injector that includes the `HeroModule`. + + + +This is generally no different from configuring the injector of the NgModule itself, +except that the service is tree-shakable if the NgModule doesn't use it. +It can be useful for a library that offers a particular service that some +components *might* want to inject optionally, +and leave it up to the app whether to provide the service. + +* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers). + + +### @NgModule-level injectors + +You can configure a provider at the module level using the `providedIn` metadata option for a non-root NgModule, in order to limit the scope of the provider to that module. +This is the equivalent of specifying the non-root module in the `@Injectable()` metadata, except that the service provided this way is not tree-shakable. + +You generally don't need to specify `AppModule` with `providedIn`, because the app's `root` injector is the `AppModule` injector. +However, if you configure a app-wide provider in the`@NgModule()` metadata for `AppModule`, +it overrides one configured for `root` in the `@Injectable()` metadata. +You can do this to configure a non-default provider of a service that is shared with multiple apps. + +Here is an example of the case where the component router configuration includes +a non-default [location strategy](guide/router#location-strategy) by listing its provider +in the `providers` list of the `AppModule`. + + + + + + +{@a register-providers-component} + +### @Component-level injectors + +Individual components within an NgModule have their own injectors. +You can limit the scope of a provider to a component and its children +by configuring the provider at the component level using the `@Component` metadata. + +The following example is a revised `HeroesComponent` that specifies `HeroService` in its `providers` array. `HeroService` can provide heroes to instances of this component, or to any child component instances. + + + + +### Element injectors + +An injector does not actually belong to a component, but rather to the component instance's anchor element in the DOM. A different component instance on a different DOM element uses a different injector. + +Components are a special type of directive, and the `providers` property of +`@Component()` is inherited from `@Directive()`. +Directives can also have dependencies, and you can configure providers +in their `@Directive()` metadata. +When you configure a provider for a component or directive using the `providers` property, that provider belongs to the injector for the anchor DOM element. Components and directives on the same element share an injector. + + + +* Learn more about [Element Injectors in Angular](https://blog.angularindepth.com/a-curios-case-of-the-host-decorator-and-element-injectors-in-angular-582562abcf0a). + + + +## Injector bubbling Consider this guide's variation on the Tour of Heroes application. -At the top is the `AppComponent` which has some sub-components. -One of them is the `HeroesListComponent`. +At the top is the `AppComponent` which has some subcomponents, such as the `HeroesListComponent`. The `HeroesListComponent` holds and manages multiple instances of the `HeroTaxReturnComponent`. -The following diagram represents the state of the this guide's three-level component tree when there are three instances of `HeroTaxReturnComponent` -open simultaneously. - +The following diagram represents the state of this three-level component tree when there are three instances of `HeroTaxReturnComponent` open simultaneously.
injector tree
- - -### Injector bubbling - When a component requests a dependency, Angular tries to satisfy that dependency with a provider registered in that component's own injector. If the component's injector lacks the provider, it passes the request up to its parent component's injector. -If that injector can't satisfy the request, it passes it along to *its* parent injector. +If that injector can't satisfy the request, it passes the request along to the next parent injector up the tree. The requests keep bubbling up until Angular finds an injector that can handle the request or runs out of ancestor injectors. -If it runs out of ancestors, Angular throws an error. +If it runs out of ancestors, Angular throws an error. + +If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to provide the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
+You can cap the bubbling by adding the `@Host()` parameter decorator on the dependant-service parameter +in a component's constructor. +The hunt for providers stops at the injector for the host element of the component. +* See an [example](guide/dependency-injection-in-action#qualify-dependency-lookup) of using `@Host` together with `@Optional`, another parameter decorator that lets you handle the null case if no provider is found. -You can cap the bubbling. An intermediate component can declare that it is the "host" component. -The hunt for providers will climb no higher than the injector for that host component. -This is a topic for another day. - +* Learn more about the [`@Host` decorator and Element Injectors](https://blog.angularindepth.com/a-curios-case-of-the-host-decorator-and-element-injectors-in-angular-582562abcf0a).
+If you only register providers with the root injector at the top level (typically the root `AppModule`), the tree of injectors appears to be flat. +All requests bubble up to the root injector, whether you configured it with the `bootstrapModule` method, or registered all providers with `root` in their own services. - -### Re-providing a service at different levels - -You can re-register a provider for a particular dependency token at multiple levels of the injector tree. -You don't *have* to re-register providers. You shouldn't do so unless you have a good reason. -But you *can*. - -As the resolution logic works upwards, the first provider encountered wins. -Thus, a provider in an intermediate injector intercepts a request for a service from something lower in the tree. -It effectively "reconfigures" and "shadows" a provider at a higher level in the tree. - -If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat. -All requests bubble up to the root NgModule injector that you configured with the `bootstrapModule` method. - - +{@a component-injectors} ## Component injectors The ability to configure one or more providers at different levels opens up interesting and useful possibilities. +The guide sample offers some scenarios where you might want to do so. ### Scenario: service isolation -Architectural reasons may lead you to restrict access to a service to the application domain where it belongs. - -The guide sample includes a `VillainsListComponent` that displays a list of villains. +Architectural reasons may lead you to restrict access to a service to the application domain where it belongs. +For example, the guide sample includes a `VillainsListComponent` that displays a list of villains. It gets those villains from a `VillainsService`. -While you _could_ provide `VillainsService` in the root `AppModule` (that's where you'll find the `HeroesService`), -that would make the `VillainsService` available everywhere in the application, including the _Hero_ workflows. +If you provide `VillainsService` in the root `AppModule` (where you registered the `HeroesService`), +that would make the `VillainsService` available everywhere in the application, including the _Hero_ workflows. If you later modified the `VillainsService`, you could break something in a hero component somewhere. Providing the service in the root `AppModule` creates that risk. -If you later modified the `VillainsService`, you could break something in a hero component somewhere. -That's not supposed to happen but providing the service in the root `AppModule` creates that risk. - -Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this: +Instead, you can provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this: - - By providing `VillainsService` in the `VillainsListComponent` metadata and nowhere else, the service becomes available only in the `VillainsListComponent` and its sub-component tree. It's still a singleton, but it's a singleton that exist solely in the _villain_ domain. @@ -144,12 +240,10 @@ Each tax return component has the following characteristics: Heroes in action
- - -One might suppose that the `HeroTaxReturnComponent` has logic to manage and restore changes. +Suppose that the `HeroTaxReturnComponent` has logic to manage and restore changes. That would be a pretty easy task for a simple hero tax return. In the real world, with a rich tax return data model, the change management would be tricky. -You might delegate that management to a helper service, as this example does. +You could delegate that management to a helper service, as this example does. Here is the `HeroTaxReturnService`. It caches a single `HeroTaxReturn`, tracks changes to that return, and can save or restore it. @@ -160,8 +254,6 @@ It also delegates to the application-wide singleton `HeroService`, which it gets - - Here is the `HeroTaxReturnComponent` that makes use of it. @@ -170,36 +262,29 @@ Here is the `HeroTaxReturnComponent` that makes use of it. - The _tax-return-to-edit_ arrives via the input property which is implemented with getters and setters. The setter initializes the component's own instance of the `HeroTaxReturnService` with the incoming return. The getter always returns what that service says is the current state of the hero. The component also asks the service to save and restore this tax return. -There'd be big trouble if _this_ service were an application-wide singleton. -Every component would share the same service instance. -Each component would overwrite the tax return that belonged to another hero. -What a mess! +This won't work if the service is an application-wide singleton. +Every component would share the same service instance, and each component would overwrite the tax return that belonged to another hero. -Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property. +To prevent this, we configure the component-level injector of `HeroTaxReturnComponent` to provide the service, using the `providers` property in the component metadata. - - The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`. Recall that every component _instance_ has its own injector. -Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service. -No tax return overwriting. No mess. +Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service, and no tax return gets overwritten.
- The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation. You can review it and download it from the . @@ -210,10 +295,9 @@ You can review it and download it from the . ### Scenario: specialized providers -Another reason to re-provide a service is to substitute a _more specialized_ implementation of that service, -deeper in the component tree. +Another reason to re-provide a service at another level is to substitute a _more specialized_ implementation of that service, deeper in the component tree. -Consider again the Car example from the [Dependency Injection](guide/dependency-injection) guide. +Consider a Car component that depends on several services. Suppose you configured the root injector (marked as A) with _generic_ providers for `CarService`, `EngineService` and `TiresService`. @@ -229,8 +313,6 @@ Component (B) is the parent of another component (C) that defines its own, even car components
- - Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself. When you resolve an instance of `Car` at the deepest component (C), @@ -247,9 +329,7 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
- The code for this _cars_ scenario is in the `car.components.ts` and `car.services.ts` files of the sample which you can review and download from the .
- diff --git a/aio/content/guide/providers.md b/aio/content/guide/providers.md index 893fca5931..ad7a065111 100644 --- a/aio/content/guide/providers.md +++ b/aio/content/guide/providers.md @@ -88,5 +88,5 @@ Register a provider with a component when you must limit a service instance to a You may also be interested in: * [Singleton Services](guide/singleton-services), which elaborates on the concepts covered on this page. * [Lazy Loading Modules](guide/lazy-loading-ngmodules). -* [Tree-shakable Providers](guide/dependency-injection#tree-shakable-providers). +* [Tree-shakable Providers](guide/dependency-injection-providers#tree-shakable-providers). * [NgModule FAQ](guide/ngmodule-faq). diff --git a/aio/content/guide/upgrade.md b/aio/content/guide/upgrade.md index 4be7b0f987..6cd607a004 100644 --- a/aio/content/guide/upgrade.md +++ b/aio/content/guide/upgrade.md @@ -768,7 +768,7 @@ code. For example, you might have a service called `HeroesService` in AngularJS: -You can upgrade the service using a Angular [factory provider](guide/dependency-injection#factory-providers) +You can upgrade the service using a Angular [factory provider](guide/dependency-injection-providers#factory-providers) that requests the service from the AngularJS `$injector`. Many developers prefer to declare the factory provider in a separate `ajs-upgraded-providers.ts` file diff --git a/aio/content/navigation.json b/aio/content/navigation.json index fb14f51ddc..b576808105 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -349,11 +349,6 @@ "title": "Dependency Injection", "tooltip": "Dependency Injection: creating and injecting services", "children": [ - { - "url": "guide/dependency-injection-pattern", - "title": "The Dependency Injection pattern", - "tooltip": "Learn about the dependency injection pattern behind the Angular DI system." - }, { "url": "guide/dependency-injection", "title": "Angular Dependency Injection", @@ -362,12 +357,22 @@ { "url": "guide/hierarchical-dependency-injection", "title": "Hierarchical Injectors", - "tooltip": "Angular's hierarchical dependency injection system supports nested injectors in parallel with the component tree." + "tooltip": "An injector tree parallels the component tree and supports nested dependencies." + }, + { + "url": "guide/dependency-injection-providers", + "title": "DI Providers", + "tooltip": "More about the different kinds of providers." }, { "url": "guide/dependency-injection-in-action", "title": "DI in Action", - "tooltip": "Techniques for Dependency Injection." + "tooltip": "Techniques for dependency injection." + }, + { + "url": "guide/dependency-injection-navtree", + "title": "Navigate the Component Tree", + "tooltip": "Use the injection tree to find parent components." } ] },