parent
5a2531ee45
commit
c03186013c
|
@ -4,7 +4,8 @@
|
||||||
"files":[
|
"files":[
|
||||||
"!**/*.d.ts",
|
"!**/*.d.ts",
|
||||||
"!**/*.js",
|
"!**/*.js",
|
||||||
"!**/*.[1,2].*"
|
"!**/*.[0,1,2].*",
|
||||||
|
"**/dummy.module.ts"
|
||||||
],
|
],
|
||||||
"tags": ["dependency", "di"]
|
"tags": ["dependency", "di"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
Must put this interface in its own file instead of app.config.ts
|
||||||
|
or else TypeScript gives a (bogus) warning:
|
||||||
|
WARNING in ./src/app/... .ts
|
||||||
|
"export 'AppConfig' was not found in './app.config'
|
||||||
|
*/
|
||||||
|
export interface AppConfig {
|
||||||
|
apiEndpoint: string;
|
||||||
|
title: string;
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
// Early versions
|
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
// #docregion imports
|
// #docregion imports
|
||||||
import { Component } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { Inject } from '@angular/core';
|
|
||||||
|
|
||||||
import { APP_CONFIG, AppConfig } from './app.config';
|
import { APP_CONFIG, AppConfig } from './app.config';
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
|
@ -23,3 +22,5 @@ export class AppComponent {
|
||||||
}
|
}
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
}
|
}
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
|
|
||||||
import { APP_CONFIG, AppConfig } from './app.config';
|
import { APP_CONFIG, AppConfig } from './app.config';
|
||||||
import { Logger } from './logger.service';
|
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
|
|
||||||
|
@ -23,8 +22,7 @@ import { UserService } from './user.service';
|
||||||
<app-heroes id="authorized" *ngIf="isAuthorized"></app-heroes>
|
<app-heroes id="authorized" *ngIf="isAuthorized"></app-heroes>
|
||||||
<app-heroes id="unauthorized" *ngIf="!isAuthorized"></app-heroes>
|
<app-heroes id="unauthorized" *ngIf="!isAuthorized"></app-heroes>
|
||||||
<app-providers></app-providers>
|
<app-providers></app-providers>
|
||||||
`,
|
`
|
||||||
providers: [Logger]
|
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
|
import { AppConfig } from './app-config';
|
||||||
|
export { AppConfig } from './app-config';
|
||||||
|
|
||||||
// #docregion token
|
// #docregion token
|
||||||
import { InjectionToken } from '@angular/core';
|
import { InjectionToken } from '@angular/core';
|
||||||
|
|
||||||
export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');
|
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
|
||||||
// #enddocregion token
|
// #enddocregion token
|
||||||
|
|
||||||
// #docregion config
|
// #docregion config
|
||||||
export interface AppConfig {
|
|
||||||
apiEndpoint: string;
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const HERO_DI_CONFIG: AppConfig = {
|
export const HERO_DI_CONFIG: AppConfig = {
|
||||||
apiEndpoint: 'api.heroes.com',
|
apiEndpoint: 'api.heroes.com',
|
||||||
title: 'Dependency Injection'
|
title: 'Dependency Injection'
|
||||||
|
|
|
@ -1,32 +1,24 @@
|
||||||
|
// #docplaster
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { CarComponent } from './car/car.component';
|
import { CarComponent } from './car/car.component';
|
||||||
import { HeroesComponent } from './heroes/heroes.component';
|
import { HeroesComponent } from './heroes/heroes.component';
|
||||||
import { HeroListComponent } from './heroes/hero-list.component';
|
import { HeroListComponent } from './heroes/hero-list.component';
|
||||||
import { InjectorComponent } from './injector.component';
|
import { InjectorComponent } from './injector.component';
|
||||||
|
import { Logger } from './logger.service';
|
||||||
import { TestComponent } from './test.component';
|
import { TestComponent } from './test.component';
|
||||||
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
|
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
import {
|
|
||||||
ProvidersComponent,
|
import { ProvidersModule } from './providers.module';
|
||||||
Provider1Component,
|
|
||||||
Provider3Component,
|
|
||||||
Provider4Component,
|
|
||||||
Provider5Component,
|
|
||||||
Provider6aComponent,
|
|
||||||
Provider6bComponent,
|
|
||||||
Provider7Component,
|
|
||||||
Provider8Component,
|
|
||||||
Provider9Component,
|
|
||||||
Provider10Component,
|
|
||||||
} from './providers.component';
|
|
||||||
|
|
||||||
// #docregion ngmodule
|
// #docregion ngmodule
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule
|
BrowserModule,
|
||||||
|
ProvidersModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
|
@ -35,26 +27,19 @@ import {
|
||||||
// #enddocregion ngmodule
|
// #enddocregion ngmodule
|
||||||
HeroListComponent,
|
HeroListComponent,
|
||||||
InjectorComponent,
|
InjectorComponent,
|
||||||
TestComponent,
|
TestComponent
|
||||||
ProvidersComponent,
|
|
||||||
Provider1Component,
|
|
||||||
Provider3Component,
|
|
||||||
Provider4Component,
|
|
||||||
Provider5Component,
|
|
||||||
Provider6aComponent,
|
|
||||||
Provider6bComponent,
|
|
||||||
Provider7Component,
|
|
||||||
Provider8Component,
|
|
||||||
Provider9Component,
|
|
||||||
Provider10Component,
|
|
||||||
// #docregion ngmodule
|
// #docregion ngmodule
|
||||||
],
|
],
|
||||||
// #docregion ngmodule-providers
|
// #docregion providers, providers-2
|
||||||
providers: [
|
providers: [
|
||||||
|
// #enddocregion providers
|
||||||
|
Logger,
|
||||||
|
// #docregion providers
|
||||||
UserService,
|
UserService,
|
||||||
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
|
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
|
||||||
],
|
],
|
||||||
// #enddocregion ngmodule-providers
|
// #enddocregion providers, providers-2
|
||||||
|
exports: [ CarComponent, HeroesComponent ],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [ AppComponent ]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
/// Dummy modules to satisfy Angular Language Service
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
////////
|
||||||
|
|
||||||
|
import { AppComponent as AppComponent1 } from './app.component.1';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ CommonModule, AppModule ],
|
||||||
|
declarations: [ AppComponent1 ]
|
||||||
|
})
|
||||||
|
export class DummyModule1 {}
|
||||||
|
|
||||||
|
/////////
|
||||||
|
|
||||||
|
import { AppComponent as AppComponent2 } from './app.component.2';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ CommonModule, AppModule ],
|
||||||
|
declarations: [ AppComponent2 ]
|
||||||
|
})
|
||||||
|
export class DummyModule2 {}
|
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
/// Dummy modules to satisfy Angular Language Service
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
////////
|
||||||
|
|
||||||
|
import { HeroListComponent as HeroListComponent1 } from './hero-list.component.1';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ CommonModule ],
|
||||||
|
declarations: [ HeroListComponent1 ],
|
||||||
|
exports: [ HeroListComponent1 ]
|
||||||
|
})
|
||||||
|
export class DummyModule1 {}
|
||||||
|
|
||||||
|
/////////
|
||||||
|
|
||||||
|
import { HeroListComponent as HeroListComponent2 } from './hero-list.component.2';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ CommonModule ],
|
||||||
|
declarations: [ HeroListComponent2 ]
|
||||||
|
})
|
||||||
|
export class DummyModule2 {}
|
||||||
|
|
||||||
|
/////////
|
||||||
|
|
||||||
|
import { HeroesComponent as HeroesComponent1 } from './heroes.component.1';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ CommonModule, DummyModule1 ],
|
||||||
|
declarations: [ HeroesComponent1 ]
|
||||||
|
})
|
||||||
|
export class DummyModule3 {}
|
|
@ -1,16 +1,17 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-list',
|
selector: 'app-hero-list',
|
||||||
template: `
|
template: `
|
||||||
<div *ngFor="let hero of heroes">
|
<div *ngFor="let hero of heroes">
|
||||||
{{hero.id}} - {{hero.name}}
|
{{hero.id}} - {{hero.name}}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
|
// #docregion class
|
||||||
export class HeroListComponent {
|
export class HeroListComponent {
|
||||||
heroes = HEROES;
|
heroes = HEROES;
|
||||||
}
|
}
|
||||||
|
// #enddocregion class
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
import { HeroService } from './hero.service.1';
|
import { HeroService } from './hero.service.1';
|
||||||
|
@ -15,9 +14,9 @@ import { HeroService } from './hero.service';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-list',
|
selector: 'app-hero-list',
|
||||||
template: `
|
template: `
|
||||||
<div *ngFor="let hero of heroes">
|
<div *ngFor="let hero of heroes">
|
||||||
{{hero.id}} - {{hero.name}}
|
{{hero.id}} - {{hero.name}}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class HeroListComponent {
|
export class HeroListComponent {
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
/* tslint:disable:one-line */
|
/* tslint:disable:one-line */
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-list',
|
selector: 'app-hero-list',
|
||||||
template: `
|
template: `
|
||||||
<div *ngFor="let hero of heroes">
|
<div *ngFor="let hero of heroes">
|
||||||
{{hero.id}} - {{hero.name}}
|
{{hero.id}} - {{hero.name}}
|
||||||
({{hero.isSecret ? 'secret' : 'public'}})
|
({{hero.isSecret ? 'secret' : 'public'}})
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
export class HeroListComponent {
|
export class HeroListComponent {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class HeroService {
|
||||||
|
constructor() { }
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
import { Logger } from '../logger.service';
|
import { Logger } from '../logger.service';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
import { Logger } from '../logger.service';
|
import { Logger } from '../logger.service';
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion full, v1
|
// #docregion, v1
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
import { HeroService } from './hero.service';
|
// #docregion v1
|
||||||
// #enddocregion full
|
|
||||||
|
|
||||||
// #docregion full, v1
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-heroes',
|
selector: 'app-heroes',
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
providers: [HeroService],
|
providers: [ HeroService ],
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
template: `
|
template: `
|
||||||
<h2>Heroes</h2>
|
<h2>Heroes</h2>
|
||||||
<app-hero-list></app-hero-list>
|
<app-hero-list></app-hero-list>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class HeroesComponent { }
|
export class HeroesComponent { }
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { heroServiceProvider } from './hero.service.provider';
|
import { heroServiceProvider } from './hero.service.provider';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-heroes',
|
selector: 'app-heroes',
|
||||||
|
providers: [ heroServiceProvider ],
|
||||||
template: `
|
template: `
|
||||||
<h2>Heroes</h2>
|
<h2>Heroes</h2>
|
||||||
<app-hero-list></app-hero-list>
|
<app-hero-list></app-hero-list>
|
||||||
`,
|
`
|
||||||
providers: [heroServiceProvider]
|
|
||||||
})
|
})
|
||||||
export class HeroesComponent { }
|
export class HeroesComponent { }
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
/* tslint:disable:one-line:check-open-brace*/
|
/*
|
||||||
// Examples of provider arrays
|
* A collection of demo components showing different ways to provide services
|
||||||
// #docplaster
|
* in @Component metadata
|
||||||
|
*/
|
||||||
import { Component, Inject, Injectable, OnInit } from '@angular/core';
|
import { Component, Inject, Injectable, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { APP_CONFIG, AppConfig,
|
import {
|
||||||
HERO_DI_CONFIG } from './app.config';
|
APP_CONFIG,
|
||||||
|
AppConfig,
|
||||||
|
HERO_DI_CONFIG } from './app.config';
|
||||||
|
|
||||||
import { HeroService } from './heroes/hero.service';
|
import { HeroService } from './heroes/hero.service';
|
||||||
import { heroServiceProvider } from './heroes/hero.service.provider';
|
import { heroServiceProvider } from './heroes/hero.service.provider';
|
||||||
import { Logger } from './logger.service';
|
import { Logger } from './logger.service';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
|
|
||||||
let template = '{{log}}';
|
const template = '{{log}}';
|
||||||
|
|
||||||
//////////////////////////////////////////
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-1',
|
selector: 'provider-1',
|
||||||
template: template,
|
template: template,
|
||||||
|
@ -30,6 +32,7 @@ export class Provider1Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-3',
|
selector: 'provider-3',
|
||||||
template: template,
|
template: template,
|
||||||
|
@ -47,7 +50,7 @@ export class Provider3Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
class BetterLogger extends Logger {}
|
export class BetterLogger extends Logger {}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-4',
|
selector: 'provider-4',
|
||||||
|
@ -66,9 +69,10 @@ export class Provider4Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
|
|
||||||
// #docregion EvenBetterLogger
|
// #docregion EvenBetterLogger
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class EvenBetterLogger extends Logger {
|
export class EvenBetterLogger extends Logger {
|
||||||
constructor(private userService: UserService) { super(); }
|
constructor(private userService: UserService) { super(); }
|
||||||
|
|
||||||
log(message: string) {
|
log(message: string) {
|
||||||
|
@ -96,8 +100,10 @@ export class Provider5Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
class NewLogger extends Logger {}
|
|
||||||
class OldLogger {
|
export class NewLogger extends Logger {}
|
||||||
|
|
||||||
|
export class OldLogger {
|
||||||
logs: string[] = [];
|
logs: string[] = [];
|
||||||
log(message: string) {
|
log(message: string) {
|
||||||
throw new Error('Should not call the old logger!');
|
throw new Error('Should not call the old logger!');
|
||||||
|
@ -149,11 +155,14 @@ export class Provider6bComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
|
|
||||||
// #docregion silent-logger
|
// #docregion silent-logger
|
||||||
// An object in the shape of the logger service
|
// An object in the shape of the logger service
|
||||||
let silentLogger = {
|
export function SilentLoggerFn() {}
|
||||||
|
|
||||||
|
const silentLogger = {
|
||||||
logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
|
logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
|
||||||
log: () => {}
|
log: SilentLoggerFn
|
||||||
};
|
};
|
||||||
// #enddocregion silent-logger
|
// #enddocregion silent-logger
|
||||||
|
|
||||||
|
@ -172,6 +181,7 @@ export class Provider7Component {
|
||||||
this.log = logger.logs[0];
|
this.log = logger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -189,6 +199,7 @@ export class Provider8Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-9',
|
selector: 'provider-9',
|
||||||
template: template,
|
template: template,
|
||||||
|
@ -218,6 +229,7 @@ export class Provider9Component implements OnInit {
|
||||||
this.log = 'APP_CONFIG Application title is ' + this.config.title;
|
this.log = 'APP_CONFIG Application title is ' + this.config.title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
// Sample providers 1 to 7 illustrate a required logger dependency.
|
// Sample providers 1 to 7 illustrate a required logger dependency.
|
||||||
// Optional logger, can be null
|
// Optional logger, can be null
|
||||||
|
@ -248,6 +260,7 @@ export class Provider10Component implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-providers',
|
selector: 'app-providers',
|
||||||
template: `
|
template: `
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Provider1Component,
|
||||||
|
Provider3Component,
|
||||||
|
Provider4Component,
|
||||||
|
Provider5Component,
|
||||||
|
Provider6aComponent,
|
||||||
|
Provider6bComponent,
|
||||||
|
Provider7Component,
|
||||||
|
Provider8Component,
|
||||||
|
Provider9Component,
|
||||||
|
Provider10Component,
|
||||||
|
ProvidersComponent,
|
||||||
|
} from './providers.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
Provider1Component,
|
||||||
|
Provider3Component,
|
||||||
|
Provider4Component,
|
||||||
|
Provider5Component,
|
||||||
|
Provider6aComponent,
|
||||||
|
Provider6bComponent,
|
||||||
|
Provider7Component,
|
||||||
|
Provider8Component,
|
||||||
|
Provider9Component,
|
||||||
|
Provider10Component,
|
||||||
|
ProvidersComponent,
|
||||||
|
],
|
||||||
|
exports: [ ProvidersComponent ]
|
||||||
|
})
|
||||||
|
export class ProvidersModule {}
|
|
@ -2,10 +2,11 @@
|
||||||
// Simulate a simple test
|
// Simulate a simple test
|
||||||
// Reader should look to the testing chapter for the real thing
|
// Reader should look to the testing chapter for the real thing
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { HeroService } from './heroes/hero.service';
|
import { Hero } from './heroes/hero';
|
||||||
import { HeroListComponent } from './heroes/hero-list.component';
|
import { HeroService } from './heroes/hero.service';
|
||||||
|
import { HeroListComponent } from './heroes/hero-list.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tests',
|
selector: 'app-tests',
|
||||||
|
@ -22,12 +23,13 @@ export class TestComponent {
|
||||||
function runTests() {
|
function runTests() {
|
||||||
|
|
||||||
// #docregion spec
|
// #docregion spec
|
||||||
let expectedHeroes = [{name: 'A'}, {name: 'B'}]
|
const expectedHeroes = [{name: 'A'}, {name: 'B'}]
|
||||||
let mockService = <HeroService> {getHeroes: () => expectedHeroes }
|
const mockService = <HeroService> {getHeroes: () => expectedHeroes }
|
||||||
|
|
||||||
it('should have heroes when HeroListComponent created', () => {
|
it('should have heroes when HeroListComponent created', () => {
|
||||||
let hlc = new HeroListComponent(mockService);
|
// Pass the mock to the constructor as the Angular injector would
|
||||||
expect(hlc.heroes.length).toEqual(expectedHeroes.length);
|
const component = new HeroListComponent(mockService);
|
||||||
|
expect(component.heroes.length).toEqual(expectedHeroes.length);
|
||||||
});
|
});
|
||||||
// #enddocregion spec
|
// #enddocregion spec
|
||||||
|
|
||||||
|
|
|
@ -5,51 +5,6 @@ Dependency Injection is a powerful pattern for managing code dependencies.
|
||||||
This cookbook explores many of the features of Dependency Injection (DI) in Angular.
|
This cookbook explores many of the features of Dependency Injection (DI) in Angular.
|
||||||
{@a toc}
|
{@a toc}
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
# Contents
|
|
||||||
|
|
||||||
* [Application-wide dependencies](guide/dependency-injection-in-action#app-wide-dependencies)
|
|
||||||
* [External module configuration](guide/dependency-injection-in-action#external-module-configuration)
|
|
||||||
* [`@Injectable()` and nested service dependencies](guide/dependency-injection-in-action#nested-dependencies)
|
|
||||||
|
|
||||||
* [`@Injectable()`](guide/dependency-injection-in-action#injectable-1)
|
|
||||||
|
|
||||||
* [Limit service scope to a component subtree](guide/dependency-injection-in-action#service-scope)
|
|
||||||
* [Multiple service instances (sandboxing)](guide/dependency-injection-in-action#multiple-service-instances)
|
|
||||||
* [Qualify dependency lookup with `@Optional()` and `@Host()`](guide/dependency-injection-in-action#qualify-dependency-lookup)
|
|
||||||
|
|
||||||
* [Demonstration](guide/dependency-injection-in-action#demonstration)
|
|
||||||
|
|
||||||
* [Inject the component's DOM element](guide/dependency-injection-in-action#component-element)
|
|
||||||
* [Define dependencies with providers](guide/dependency-injection-in-action#providers)
|
|
||||||
|
|
||||||
* [Defining providers](guide/dependency-injection-in-action#defining-providers)
|
|
||||||
* [The *provide* object literal](guide/dependency-injection-in-action#provide)
|
|
||||||
* [`useValue`—the *value provider*](guide/dependency-injection-in-action#usevalue)
|
|
||||||
* [`useClass`—the *class provider*](guide/dependency-injection-in-action#useclass)
|
|
||||||
* [`useExisting`—the *alias provider*](guide/dependency-injection-in-action#useexisting)
|
|
||||||
* [`useFactory`—the *factory provider*](guide/dependency-injection-in-action#usefactory)
|
|
||||||
|
|
||||||
* [Provider token alternatives: the class-interface and `InjectionToken`](guide/dependency-injection-in-action#tokens)
|
|
||||||
|
|
||||||
* [class-interface](guide/dependency-injection-in-action#class-interface)
|
|
||||||
* [`InjectionToken`](guide/dependency-injection-in-action#injection-token)
|
|
||||||
|
|
||||||
* [Inject into a derived class](guide/dependency-injection-in-action#di-inheritance)
|
|
||||||
* [Find a parent component by injection](guide/dependency-injection-in-action#find-parent)
|
|
||||||
|
|
||||||
* [Find parent with a known component type](guide/dependency-injection-in-action#known-parent)
|
|
||||||
* [Cannot find a parent by its base class](guide/dependency-injection-in-action#base-parent)
|
|
||||||
* [Find a parent by its class-interface](guide/dependency-injection-in-action#class-interface-parent)
|
|
||||||
* [Find a parent in a tree of parents with `@SkipSelf()`](guide/dependency-injection-in-action#parent-tree)
|
|
||||||
* [The `Parent` class-interface](guide/dependency-injection-in-action#parent-token)
|
|
||||||
* [A `provideParent()` helper function](guide/dependency-injection-in-action#provideparent)
|
|
||||||
|
|
||||||
* [Break circularities with a forward class reference (*forwardRef*)](guide/dependency-injection-in-action#forwardref)
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
See the <live-example name="dependency-injection-in-action"></live-example>
|
See the <live-example name="dependency-injection-in-action"></live-example>
|
||||||
of the code in this cookbook.
|
of the code in this cookbook.
|
||||||
|
|
||||||
|
@ -79,7 +34,7 @@ is all the registration you need.
|
||||||
|
|
||||||
A *provider* is something that can create or deliver a service.
|
A *provider* is something that can create or deliver a service.
|
||||||
Angular creates a service instance from a class provider by using `new`.
|
Angular creates a service instance from a class provider by using `new`.
|
||||||
Read more about providers in the [Dependency Injection](guide/dependency-injection#injector-providers)
|
Read more about providers in the [Dependency Injection](guide/dependency-injection#register-providers-ngmodule)
|
||||||
guide.
|
guide.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
# 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:
|
||||||
|
|
||||||
|
<code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car" title="src/app/car/car.ts (without DI)">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
<code-tabs>
|
||||||
|
|
||||||
|
<code-pane title="src/app/car/car.ts (excerpt with DI)" path="dependency-injection/src/app/car/car.ts" region="car-ctor">
|
||||||
|
</code-pane>
|
||||||
|
|
||||||
|
<code-pane title="src/app/car/car.ts (excerpt without DI)" path="dependency-injection/src/app/car/car-no-di.ts" region="car-ctor">
|
||||||
|
</code-pane>
|
||||||
|
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
This example leverages TypeScript's constructor syntax for declaring
|
||||||
|
parameters and properties simultaneously.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Now you can create a car by passing the engine and tires to the constructor.
|
||||||
|
|
||||||
|
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation" linenums="false">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
|
||||||
|
something like this:
|
||||||
|
|
||||||
|
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-param" linenums="false">
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
The critical point is this: the `Car` class did not have to change.
|
||||||
|
You'll take care of the consumer's problem shortly.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
**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:
|
||||||
|
|
||||||
|
<code-example path="dependency-injection/src/app/car/car-factory.ts" title="src/app/car/car-factory.ts">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" title="src/app/car/car-injector.ts" linenums="false">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
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.
|
File diff suppressed because it is too large
Load Diff
|
@ -243,10 +243,15 @@
|
||||||
"title": "Dependency Injection",
|
"title": "Dependency Injection",
|
||||||
"tooltip": "Dependency Injection: creating and injecting services",
|
"tooltip": "Dependency Injection: creating and injecting services",
|
||||||
"children": [
|
"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",
|
"url": "guide/dependency-injection",
|
||||||
"title": "Dependency Injection",
|
"title": "Angular Dependency Injection",
|
||||||
"tooltip": "Angular's dependency injection system creates and delivers dependent services \"just-in-time\"."
|
"tooltip": "Angular's dependency injection system creates and delivers dependent services to Angular-created classes."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "guide/hierarchical-dependency-injection",
|
"url": "guide/hierarchical-dependency-injection",
|
||||||
|
|
Loading…
Reference in New Issue