diff --git a/gulpfile.js b/gulpfile.js index 05986b71c5..4b83e9838e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1076,7 +1076,7 @@ function getChangedExamplesForCommit(commit, relativePath) { return commit.getDiff().then(function(diffList) { var filePaths = []; diffList.forEach(function (diff) { - diff.patches().forEach(function (patch) { + diff.patches().then(function (patch) { if (patch.isAdded() || patch.isModified) { var filePath = path.normalize(patch.newFile().path()); var isExample = filePath.indexOf(relativePath) >= 0; diff --git a/package.json b/package.json index fd0083db03..b8513be3da 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "minimatch": "^2.0.10", "mkdirp": "^0.5.1", "node-html-encoder": "0.0.2", - "nodegit": "0.5.0", + "nodegit": "0.13.0", "path": "^0.11.14", "prompt": "^0.2.14", "protractor": "^3.0.0", diff --git a/public/docs/_examples/cb-dependency-injection/e2e-spec.ts b/public/docs/_examples/cb-dependency-injection/e2e-spec.ts index 2beb08af94..29a8822110 100644 --- a/public/docs/_examples/cb-dependency-injection/e2e-spec.ts +++ b/public/docs/_examples/cb-dependency-injection/e2e-spec.ts @@ -35,16 +35,11 @@ describe('Dependency Injection Cookbook', function () { expect(sortedHero).toBeDefined(); }); - it('should render Hero of the Month when DI deps are defined using provide()', function () { + it('should render Hero of the Month', function () { let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0); expect(heroOfTheMonth).toBeDefined(); }); - it('should render Hero of the Month when DI deps are defined using provide object literal', function () { - let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month 2"]')).get(0); - expect(heroOfTheMonth).toBeDefined(); - }); - it('should render Hero Bios', function () { let heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0); expect(heroBios).toBeDefined(); @@ -60,16 +55,11 @@ describe('Dependency Injection Cookbook', function () { expect(magmaPhone).toBeDefined(); }); - it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide()', function () { + it('should render Hero-of-the-Month runner-ups', function () { let runnersUp = element(by.id('rups1')).getText(); expect(runnersUp).toContain('RubberMan, Mr. Nice'); }); - it('should render Hero-of-the-Month runner-ups when DI deps are defined using provide object literal', function () { - let runnersUp = element(by.id('rups2')).getText(); - expect(runnersUp).toContain('RubberMan, Mr. Nice'); - }); - it('should render DateLogger log entry in Hero-of-the-Month', function () { let logs = element.all(by.id('logs')).get(0).getText(); expect(logs).toContain('INFO: starting up at'); diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/app.component.html b/public/docs/_examples/cb-dependency-injection/ts/app/app.component.html index 0e1d15932d..a715e484fe 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/app.component.html +++ b/public/docs/_examples/cb-dependency-injection/ts/app/app.component.html @@ -23,10 +23,6 @@ -
- -
-

Unsorted Heroes

diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/app.component.ts b/public/docs/_examples/cb-dependency-injection/ts/app/app.component.ts index 7188e4f63f..a72af9e6da 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/app.component.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/app.component.ts @@ -9,13 +9,10 @@ import { HeroesBaseComponent, import { HighlightDirective } from './highlight.directive'; import { ParentFinderComponent } from './parent-finder.component'; -// Object Literal syntax -import { HeroOfTheMonthLiteralsComponent } from './hero-of-the-month-literals.component'; - const DIRECTIVES = [ HeroBiosComponent, HeroBiosAndContactsComponent, HeroesBaseComponent, SortedHeroesComponent, - HeroOfTheMonthComponent, HeroOfTheMonthLiteralsComponent, + HeroOfTheMonthComponent, HighlightDirective, ParentFinderComponent ]; diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/hero-of-the-month-literals.component.ts b/public/docs/_examples/cb-dependency-injection/ts/app/hero-of-the-month-literals.component.ts deleted file mode 100644 index 13c6c0ab9d..0000000000 --- a/public/docs/_examples/cb-dependency-injection/ts/app/hero-of-the-month-literals.component.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* tslint:disable:one-line:check-open-brace*/ -// #docplaster -// #docregion opaque-token -import { OpaqueToken } from '@angular/core'; - -export const TITLE = new OpaqueToken('title'); -// #enddocregion opaque-token - -// #docregion hero-of-the-month -import { Component, Inject } from '@angular/core'; - -import { DateLoggerService, - MinimalLogger } from './date-logger.service'; -import { Hero } from './hero'; -import { HeroService } from './hero.service'; -import { LoggerService } from './logger.service'; -import { RUNNERS_UP, - runnersUpFactory } from './runners-up'; - -// #enddocregion hero-of-the-month -// #docregion some-hero -const someHero = new Hero(42, 'Magma', 'Had a great month!', '555-555-5555'); -// #enddocregion some-hero - -const template = ` -

{{title}}

-
Winner: {{heroOfTheMonth.name}}
-
Reason for award: {{heroOfTheMonth.description}}
-
Runners-up: {{runnersUp}}
- -

Logs:

-
-
{{log}}
-
- `; - -// #docregion hero-of-the-month -@Component({ - selector: 'hero-of-the-month-lit', - template: template, - // #docregion providers-using-object-literals - providers: [ - {provide: Hero, useValue: someHero}, - {provide: TITLE, useValue: 'Hero of the Month - Object Literals'}, - {provide: HeroService, useClass: HeroService}, - {provide: LoggerService, useClass: DateLoggerService}, - {provide: MinimalLogger, useExisting: LoggerService}, - {provide: RUNNERS_UP, useFactory: runnersUpFactory(2), deps: [Hero, HeroService]} - ] - // #enddocregion providers-using-object-literals -}) -export class HeroOfTheMonthLiteralsComponent { - logs: string[] = []; - -// #docregion ctor-signature - constructor( - logger: MinimalLogger, - public heroOfTheMonth: Hero, - @Inject(RUNNERS_UP) public runnersUp: string, - @Inject(TITLE) public title: string) -// #enddocregion ctor-signature - { - this.logs = logger.logs; - logger.logInfo('starting up'); - } -} -// #enddocregion hero-of-the-month diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/hero-of-the-month.component.ts b/public/docs/_examples/cb-dependency-injection/ts/app/hero-of-the-month.component.ts index 4183734753..c5bafca36d 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/hero-of-the-month.component.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/hero-of-the-month.component.ts @@ -7,7 +7,7 @@ export const TITLE = new OpaqueToken('title'); // #enddocregion opaque-token // #docregion hero-of-the-month -import { Component, Inject, provide } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { DateLoggerService, MinimalLogger } from './date-logger.service'; @@ -40,20 +40,20 @@ const template = ` template: template, providers: [ // #docregion use-value - provide(Hero, {useValue: someHero}), + { provide: Hero, useValue: someHero }, // #docregion provide-opaque-token - provide(TITLE, {useValue: 'Hero of the Month'}), + { provide: TITLE, useValue: 'Hero of the Month' }, // #enddocregion provide-opaque-token // #enddocregion use-value // #docregion use-class - provide(HeroService, {useClass: HeroService}), - provide(LoggerService, {useClass: DateLoggerService}), + { provide: HeroService, useClass: HeroService }, + { provide: LoggerService, useClass: DateLoggerService }, // #enddocregion use-class // #docregion use-existing - provide(MinimalLogger, {useExisting: LoggerService}), + { provide: MinimalLogger, useExisting: LoggerService }, // #enddocregion use-existing // #docregion provide-opaque-token, use-factory - provide(RUNNERS_UP, {useFactory: runnersUpFactory(2), deps: [Hero, HeroService]}) + { provide: RUNNERS_UP, useFactory: runnersUpFactory(2), deps: [Hero, HeroService] } // #enddocregion provide-opaque-token, use-factory ] }) diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/main.ts b/public/docs/_examples/cb-dependency-injection/ts/app/main.ts index 595971625e..fd4646a055 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/main.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/main.ts @@ -1,6 +1,5 @@ // #docregion import { bootstrap } from '@angular/platform-browser-dynamic'; -import { provide } from '@angular/core'; import { XHRBackend } from '@angular/http'; import { ROUTER_PROVIDERS } from '@angular/router-deprecated'; import { LocationStrategy, @@ -15,10 +14,9 @@ import { AppComponent } from './app.component'; // #docregion bootstrap bootstrap(AppComponent, [ ROUTER_PROVIDERS, - provide(LocationStrategy, - {useClass: HashLocationStrategy}), + { provide: LocationStrategy, useClass: HashLocationStrategy }, - provide(XHRBackend, { useClass: InMemoryBackendService }), // in-mem server - provide(SEED_DATA, { useClass: HeroData }) // in-mem server data + { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server + { provide: SEED_DATA, useClass: HeroData } // in-mem server data ]).catch((err: any) => console.error(err)); // #enddocregion bootstrap diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/parent-finder.component.ts b/public/docs/_examples/cb-dependency-injection/ts/app/parent-finder.component.ts index 7cfc460600..045ce8c545 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/parent-finder.component.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/parent-finder.component.ts @@ -22,14 +22,17 @@ const DifferentParent = Parent; const provideParent = // #enddocregion provide-parent, provide-the-parent // #docregion provide-parent - (component: any, parentType?: any) => - provide(parentType || Parent, { useExisting: forwardRef(() => component) }); + (component: any, parentType?: any) => { + return { provide: parentType || Parent, useExisting: forwardRef(() => component) } + }; // #enddocregion provide-parent // Simpler syntax version that always provides the component in the name of `Parent`. const provideTheParent = // #docregion provide-the-parent - (component: any) => provide(Parent, { useExisting: forwardRef(() => component) }); + (component: any) => { + return { provide: Parent, useExisting: forwardRef(() => component) } + }; // #enddocregion provide-the-parent @@ -105,7 +108,7 @@ const templateB = ` selector: 'barry', template: templateB, directives: C_DIRECTIVES, - providers: [ provide(Parent, { useExisting: forwardRef(() => BarryComponent) }) ] + providers: [{ provide: Parent, useExisting: forwardRef(() => BarryComponent) }] }) export class BarryComponent implements Parent { name = 'Barry'; @@ -155,7 +158,7 @@ const B_DIRECTIVES = [ BarryComponent, BethComponent, BobComponent ];
`, // #enddocregion alex-1 // #docregion alex-providers - providers: [ provide(Parent, { useExisting: forwardRef(() => AlexComponent) }) ], + providers: [{ provide: Parent, useExisting: forwardRef(() => AlexComponent) }], // #enddocregion alex-providers // #docregion alex-1 directives: C_DIRECTIVES diff --git a/public/docs/_examples/cb-ts-to-js/js/app/main.js b/public/docs/_examples/cb-ts-to-js/js/app/main.js index ed92d6f823..f483a7c8cd 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/main.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/main.js @@ -4,8 +4,6 @@ // #enddocregion appimport // #docregion ng2import - var provide = - ng.core.provide; var bootstrap = ng.platformBrowserDynamic.bootstrap; var LocationStrategy = @@ -25,10 +23,10 @@ bootstrap(app.HeroDIComponent, [app.DataService]); bootstrap(app.HeroDIInlineComponent, [app.DataService]); bootstrap(app.HeroDIInjectComponent, [ - ng.core.provide('heroName', {useValue: 'Windstorm'}) + { provide: 'heroName', useValue: 'Windstorm' } ]); bootstrap(app.HeroDIInjectComponent2, [ - ng.core.provide('heroName', {useValue: 'Bombasto'}) + { provide: 'heroName', useValue: 'Bombasto' } ]); bootstrap(app.HeroDIInjectAdditionalComponent); bootstrap(app.HeroIOComponent); diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/main.ts b/public/docs/_examples/cb-ts-to-js/ts/app/main.ts index 74befc103d..ea8f8377e1 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/main.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/main.ts @@ -1,6 +1,4 @@ // #docregion ng2import -import { provide } - from '@angular/core'; import { bootstrap } from '@angular/platform-browser-dynamic'; import { @@ -29,7 +27,7 @@ bootstrap(HeroComponent); bootstrap(HeroLifecycleComponent); bootstrap(HeroDIComponent, [DataService]); bootstrap(HeroDIInjectComponent, [ - provide('heroName', {useValue: 'Windstorm'}) + { provide: 'heroName', useValue: 'Windstorm' } ]); bootstrap(AppDIInjectAdditionalComponent); bootstrap(AppIOComponent); diff --git a/public/docs/_examples/dependency-injection/dart/lib/providers_component.dart b/public/docs/_examples/dependency-injection/dart/lib/providers_component.dart index 898ad1b001..11f2184242 100644 --- a/public/docs/_examples/dependency-injection/dart/lib/providers_component.dart +++ b/public/docs/_examples/dependency-injection/dart/lib/providers_component.dart @@ -27,38 +27,6 @@ class ProviderComponent1 { } } -@Component( - selector: 'provider-2', - template: '{{log}}', - providers: - // #docregion providers-2 - const [const Provider(Logger, useClass: Logger)] - // #enddocregion providers-2 -) -class ProviderComponent2 { - String log; - - ProviderComponent2(Logger logger) { - logger.log('Hello from logger provided with Provider class and useClass'); - log = logger.logs[0]; - } -} - -/// Component just used to ensure that shared E2E tests pass. -@Component( - selector: 'provider-3', - template: '{{log}}', - providers: const [const Provider(Logger, useClass: Logger)] -) -class ProviderComponent3 { - String log; - - ProviderComponent3(Logger logger) { - logger.log('Hello from logger provided with useClass'); - log = logger.logs[0]; - } -} - /// Component just used to ensure that shared E2E tests pass. @Component( selector: 'provider-3a', @@ -282,8 +250,6 @@ class ProviderComponent10 implements OnInit { template: '''

Provider variations

-
-
@@ -295,8 +261,6 @@ class ProviderComponent10 implements OnInit {
''', directives: const [ ProviderComponent1, - ProviderComponent2, - ProviderComponent3, ProviderComponent3a, ProviderComponent4, ProviderComponent5, diff --git a/public/docs/_examples/dependency-injection/e2e-spec.ts b/public/docs/_examples/dependency-injection/e2e-spec.ts index 5e8fd38db8..1f7226867d 100644 --- a/public/docs/_examples/dependency-injection/e2e-spec.ts +++ b/public/docs/_examples/dependency-injection/e2e-spec.ts @@ -80,18 +80,8 @@ describe('Dependency Injection Tests', function () { expect(element(by.css('#p1')).getText()).toEqual(expectedMsg); }); - it('P2 (Provider) displays as expected', function () { - expectedMsg = 'Hello from logger provided with Provider class and useClass'; - expect(element(by.css('#p2')).getText()).toEqual(expectedMsg); - }); - - it('P3 (provide) displays as expected', function () { - expectedMsg = 'Hello from logger provided with useClass'; - expect(element(by.css('#p3')).getText()).toEqual(expectedMsg); - }); - it('P3a (provide) displays as expected', function () { - expectedMsg = 'Hello from logger provided with {provide: Logger, useClass: Logger}'; + expectedMsg = 'Hello from logger provided with { provide: Logger, useClass: Logger }'; expect(element(by.css('#p3a')).getText()).toEqual(expectedMsg); }); diff --git a/public/docs/_examples/dependency-injection/ts/app/app.component.2.ts b/public/docs/_examples/dependency-injection/ts/app/app.component.2.ts index 582eb7474b..78e3f5483e 100644 --- a/public/docs/_examples/dependency-injection/ts/app/app.component.2.ts +++ b/public/docs/_examples/dependency-injection/ts/app/app.component.2.ts @@ -4,7 +4,7 @@ import { Component } from '@angular/core'; import { CarComponent } from './car/car.component'; import { HeroesComponent } from './heroes/heroes.component.1'; -import { provide, Inject } from '@angular/core'; +import { Inject } from '@angular/core'; import { APP_CONFIG, AppConfig, HERO_DI_CONFIG } from './app.config'; import { Logger } from './logger.service'; @@ -21,7 +21,7 @@ import { Logger } from './logger.service'; providers: [ Logger, // #docregion providers - provide(APP_CONFIG, {useValue: HERO_DI_CONFIG}) + { provide: APP_CONFIG, useValue: HERO_DI_CONFIG } // #enddocregion providers ] }) diff --git a/public/docs/_examples/dependency-injection/ts/app/app.component.ts b/public/docs/_examples/dependency-injection/ts/app/app.component.ts index 1d7481a19f..659d5c1faf 100644 --- a/public/docs/_examples/dependency-injection/ts/app/app.component.ts +++ b/public/docs/_examples/dependency-injection/ts/app/app.component.ts @@ -1,7 +1,7 @@ // #docplaster // #docregion // #docregion imports -import { Component, Inject, provide } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { CarComponent } from './car/car.component'; import { HeroesComponent } from './heroes/heroes.component'; @@ -37,7 +37,7 @@ import { ProvidersComponent } from './providers.component'; providers: [ Logger, UserService, - provide(APP_CONFIG, {useValue: HERO_DI_CONFIG}) + { provide: APP_CONFIG, useValue: HERO_DI_CONFIG } ] // #enddocregion providers }) diff --git a/public/docs/_examples/dependency-injection/ts/app/heroes/hero.service.provider.ts b/public/docs/_examples/dependency-injection/ts/app/heroes/hero.service.provider.ts index 861b0446b0..557e9b9534 100644 --- a/public/docs/_examples/dependency-injection/ts/app/heroes/hero.service.provider.ts +++ b/public/docs/_examples/dependency-injection/ts/app/heroes/hero.service.provider.ts @@ -1,6 +1,4 @@ // #docregion -import { provide } from '@angular/core'; - import { HeroService } from './hero.service'; import { Logger } from '../logger.service'; import { UserService } from '../user.service'; @@ -13,8 +11,8 @@ let heroServiceFactory = (logger: Logger, userService: UserService) => { // #docregion provider export let heroServiceProvider = - provide(HeroService, { + { provide: HeroService, useFactory: heroServiceFactory, deps: [Logger, UserService] - }); + }; // #enddocregion provider diff --git a/public/docs/_examples/dependency-injection/ts/app/providers.component.ts b/public/docs/_examples/dependency-injection/ts/app/providers.component.ts index 7bbc3955b8..640247d4b6 100644 --- a/public/docs/_examples/dependency-injection/ts/app/providers.component.ts +++ b/public/docs/_examples/dependency-injection/ts/app/providers.component.ts @@ -1,8 +1,7 @@ /* tslint:disable:one-line:check-open-brace*/ // Examples of provider arrays // #docplaster -import { Component, Inject, Injectable, - provide, Provider } from '@angular/core'; +import { Component, Inject, Injectable } from '@angular/core'; import { APP_CONFIG, AppConfig, HERO_DI_CONFIG } from './app.config'; @@ -30,53 +29,19 @@ export class ProviderComponent1 { } } -////////////////////////////////////////// -@Component({ - selector: 'provider-2', - template: template, - providers: - // #docregion providers-2 - [new Provider(Logger, {useClass: Logger})] - // #enddocregion providers-2 -}) -export class ProviderComponent2 { - log: string; - constructor(logger: Logger) { - logger.log('Hello from logger provided with Provider class and useClass'); - this.log = logger.logs[0]; - } -} - -////////////////////////////////////////// -@Component({ - selector: 'provider-3', - template: template, - providers: - // #docregion providers-3 - [provide(Logger, {useClass: Logger})] - // #enddocregion providers-3 -}) -export class ProviderComponent3 { - log: string; - constructor(logger: Logger) { - logger.log('Hello from logger provided with useClass'); - this.log = logger.logs[0]; - } -} - ////////////////////////////////////////// @Component({ selector: 'provider-3a', template: template, providers: // #docregion providers-3a - [{provide: Logger, useClass: Logger}] + [{ provide: Logger, useClass: Logger }] // #enddocregion providers-3a }) export class ProviderComponent3a { log: string; constructor(logger: Logger) { - logger.log('Hello from logger provided with {provide: Logger, useClass: Logger}'); + logger.log('Hello from logger provided with { provide: Logger, useClass: Logger }'); this.log = logger.logs[0]; } } @@ -89,7 +54,7 @@ class BetterLogger extends Logger {} template: template, providers: // #docregion providers-4 - [provide(Logger, {useClass: BetterLogger})] + [{ provide: Logger, useClass: BetterLogger }] // #enddocregion providers-4 }) export class ProviderComponent4 { @@ -119,7 +84,7 @@ class EvenBetterLogger extends Logger { providers: // #docregion providers-5 [ UserService, - provide(Logger, {useClass: EvenBetterLogger}) ] + { provide: Logger, useClass: EvenBetterLogger }] // #enddocregion providers-5 }) export class ProviderComponent5 { @@ -146,7 +111,7 @@ class OldLogger { // #docregion providers-6a [ NewLogger, // Not aliased! Creates two instances of `NewLogger` - provide(OldLogger, {useClass: NewLogger}) ] + { provide: OldLogger, useClass: NewLogger}] // #enddocregion providers-6a }) export class ProviderComponent6a { @@ -169,7 +134,7 @@ export class ProviderComponent6a { // #docregion providers-6b [ NewLogger, // Alias OldLogger w/ reference to NewLogger - provide(OldLogger, {useExisting: NewLogger}) ] + { provide: OldLogger, useExisting: NewLogger}] // #enddocregion providers-6b }) export class ProviderComponent6b { @@ -197,7 +162,7 @@ let silentLogger = { template: template, providers: // #docregion providers-7 - [provide(Logger, {useValue: silentLogger})] + [{ provide: Logger, useValue: silentLogger }] // #enddocregion providers-7 }) export class ProviderComponent7 { @@ -230,11 +195,11 @@ export class ProviderComponent8 { /* // #docregion providers-9-interface // FAIL! Can't use interface as provider token - [provide(AppConfig, {useValue: HERO_DI_CONFIG})] + [{ provide: AppConfig, useValue: HERO_DI_CONFIG })] // #enddocregion providers-9-interface */ // #docregion providers-9 - providers: [provide(APP_CONFIG, {useValue: HERO_DI_CONFIG})] + providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }] // #enddocregion providers-9 }) export class ProviderComponent9 { @@ -257,7 +222,7 @@ export class ProviderComponent9 { // Sample providers 1 to 7 illustrate a required logger dependency. // Optional logger, can be null // #docregion import-optional -import {Optional} from '@angular/core'; +import { Optional } from '@angular/core'; // #enddocregion import-optional let some_message: string = 'Hello from the injected logger'; @@ -286,8 +251,6 @@ export class ProviderComponent10 { template: `

Provider variations

-
-
@@ -300,8 +263,6 @@ export class ProviderComponent10 { `, directives: [ ProviderComponent1, - ProviderComponent2, - ProviderComponent3, ProviderComponent3a, ProviderComponent4, ProviderComponent5, diff --git a/public/docs/_examples/router-deprecated/ts/app/main.2.ts b/public/docs/_examples/router-deprecated/ts/app/main.2.ts index 9a04fa8109..74862cdd0a 100644 --- a/public/docs/_examples/router-deprecated/ts/app/main.2.ts +++ b/public/docs/_examples/router-deprecated/ts/app/main.2.ts @@ -8,7 +8,6 @@ import { bootstrap } from '@angular/platform-browser-dynamic'; import { ROUTER_PROVIDERS } from '@angular/router-deprecated'; // Add these symbols to override the `LocationStrategy` -import { provide } from '@angular/core'; import { LocationStrategy, HashLocationStrategy } from '@angular/common'; @@ -27,7 +26,6 @@ import { AppComponent as ac } from './app.component.2'; bootstrap(ac, [ // #docregion ROUTER_PROVIDERS, - provide(LocationStrategy, - {useClass: HashLocationStrategy}) // .../#/crisis-center/ + { provide: LocationStrategy, useClass: HashLocationStrategy } // .../#/crisis-center/ ]); // #enddocregion diff --git a/public/docs/_examples/router/ts/app/main.2.ts b/public/docs/_examples/router/ts/app/main.2.ts index 3b604f1aca..0740a43a08 100644 --- a/public/docs/_examples/router/ts/app/main.2.ts +++ b/public/docs/_examples/router/ts/app/main.2.ts @@ -8,7 +8,6 @@ import { bootstrap } from '@angular/platform-browser-dynamic'; import { ROUTER_PROVIDERS } from '@angular/router'; // Add these symbols to override the `LocationStrategy` -import { provide } from '@angular/core'; import { LocationStrategy, HashLocationStrategy } from '@angular/common'; @@ -27,7 +26,6 @@ import {AppComponent as ac} from './app.component.2'; bootstrap(ac, [ // #docregion ROUTER_PROVIDERS, - provide(LocationStrategy, - {useClass: HashLocationStrategy}) // .../#/crisis-center/ + { provide: LocationStrategy, useClass: HashLocationStrategy } // .../#/crisis-center/ ]); // #enddocregion diff --git a/public/docs/_examples/server-communication/ts/app/main.ts b/public/docs/_examples/server-communication/ts/app/main.ts index ceae9a4825..194ab5c5fb 100644 --- a/public/docs/_examples/server-communication/ts/app/main.ts +++ b/public/docs/_examples/server-communication/ts/app/main.ts @@ -1,7 +1,6 @@ // #docplaster // #docregion final // Imports for loading & configuring the in-memory web api -import { provide } from '@angular/core'; import { XHRBackend } from '@angular/http'; import { InMemoryBackendService, @@ -24,7 +23,7 @@ bootstrap(AppComponent, [ HTTP_PROVIDERS ]); // #docregion final bootstrap(AppComponent, [ HTTP_PROVIDERS, - provide(XHRBackend, { useClass: InMemoryBackendService }), // in-mem server - provide(SEED_DATA, { useClass: HeroData }) // in-mem server data + { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server + { provide: SEED_DATA, useClass: HeroData } // in-mem server data ]); // #enddocregion final diff --git a/public/docs/_examples/testing/ts/app/app.component.spec.ts b/public/docs/_examples/testing/ts/app/app.component.spec.ts index 2b53c38dcc..ce17ae744a 100644 --- a/public/docs/_examples/testing/ts/app/app.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/app.component.spec.ts @@ -2,7 +2,7 @@ import { AppComponent } from './app.component'; import { By } from '@angular/platform-browser'; -import { DebugElement, provide } from '@angular/core'; +import { DebugElement } from '@angular/core'; import { beforeEach, beforeEachProviders, @@ -28,8 +28,8 @@ describe('AppComponent', () => { .overrideDirective(AppComponent, RouterLink, MockRouterLink) .overrideDirective(AppComponent, RouterOutlet, MockRouterOutlet) .overrideProviders(AppComponent, [ - provide(HeroService, {useClass: MockHeroService}), - provide(Router, {useClass: MockRouter}), + { provide: HeroService, useClass: MockHeroService}, + { provide: Router, useClass: MockRouter}, ]) .createAsync(AppComponent) .then(fix => { diff --git a/public/docs/_examples/testing/ts/app/app.component.ts b/public/docs/_examples/testing/ts/app/app.component.ts index bf10177937..f2da2da067 100644 --- a/public/docs/_examples/testing/ts/app/app.component.ts +++ b/public/docs/_examples/testing/ts/app/app.component.ts @@ -13,6 +13,8 @@ import { HeroesComponent } from './heroes.component'; import { HeroDetailComponent } from './hero-detail.component'; import { HeroService } from './hero.service'; +import { BAG_DIRECTIVES, BAG_PROVIDERS } from './bag'; + @Component({ selector: 'my-app', template: ` @@ -22,12 +24,23 @@ import { HeroService } from './hero.service'; Heroes +
+

Bag-a-specs

+ +

External Template Comp

+ +

Comp With External Template Comp

+ `, + /* + + */ styleUrls: ['app/app.component.css'], - directives: [RouterLink, RouterOutlet], + directives: [RouterLink, RouterOutlet, BAG_DIRECTIVES], providers: [ ROUTER_PROVIDERS, - HeroService + HeroService, + BAG_PROVIDERS ] }) @RouteConfig([ diff --git a/public/docs/_examples/testing/ts/app/bad-tests.spec.ts b/public/docs/_examples/testing/ts/app/bad-tests.spec.ts index eaeab08128..1251185160 100644 --- a/public/docs/_examples/testing/ts/app/bad-tests.spec.ts +++ b/public/docs/_examples/testing/ts/app/bad-tests.spec.ts @@ -4,7 +4,7 @@ * Tests that show what goes wrong when the tests are incorrectly written or have a problem */ import { - BadTemplateUrl, ButtonComp, + BadTemplateUrlComp, ButtonComp, ChildChildComp, ChildComp, ChildWithChildComp, ExternalTemplateComp, FancyService, MockFancyService, @@ -27,7 +27,6 @@ import { import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; -import { provide } from '@angular/core'; import { ViewMetadata } from '@angular/core'; import { Observable } from 'rxjs/Rx'; @@ -134,7 +133,7 @@ xdescribe('async & inject testing errors', () => { it('should fail with an error from a promise', async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - tcb.createAsync(BadTemplateUrl); + tcb.createAsync(BadTemplateUrlComp); }))); itPromise.then( @@ -145,7 +144,7 @@ xdescribe('async & inject testing errors', () => { }, 10000); describe('using beforeEachProviders', () => { - beforeEachProviders(() => [provide(FancyService, {useValue: new FancyService()})]); + beforeEachProviders(() => [{ provide: FancyService, useValue: new FancyService() }]); beforeEach( inject([FancyService], (service: FancyService) => { expect(service.value).toEqual('real value'); })); @@ -155,7 +154,7 @@ xdescribe('async & inject testing errors', () => { it('should fail when the injector has already been used', () => { patchJasmineBeforeEach(); expect(() => { - beforeEachProviders(() => [provide(FancyService, {useValue: new FancyService()})]); + beforeEachProviders(() => [{ provide: FancyService, useValue: new FancyService() }]); }) .toThrowError('beforeEachProviders was called after the injector had been used ' + 'in a beforeEach or it block. This invalidates the test injector'); diff --git a/public/docs/_examples/testing/ts/app/bag.spec.ts b/public/docs/_examples/testing/ts/app/bag.spec.ts index c6eae1bc77..91e689e8b4 100644 --- a/public/docs/_examples/testing/ts/app/bag.spec.ts +++ b/public/docs/_examples/testing/ts/app/bag.spec.ts @@ -25,7 +25,6 @@ import { import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; -import { provide } from '@angular/core'; import { ViewMetadata } from '@angular/core'; import { Observable } from 'rxjs/Rx'; @@ -116,7 +115,7 @@ describe('using the test injector with the inject helper', () => { describe('setting up Providers with FancyService', () => { beforeEachProviders(() => [ - provide(FancyService, {useValue: new FancyService()}) + { provide: FancyService, useValue: new FancyService() } ]); it('should use FancyService', @@ -183,7 +182,7 @@ describe('using the test injector with the inject helper', () => { describe('using `withProviders` for per-test provision', () => { it('should inject test-local FancyService for this test', // `withProviders`: set up providers at individual test level - withProviders(() => [provide(FancyService, {useValue: {value: 'fake value'}})]) + withProviders(() => [{ provide: FancyService, useValue: {value: 'fake value' }}]) // now inject and test .inject([FancyService], (service: FancyService) => { @@ -314,7 +313,7 @@ describe('test component builder', function() { tcb.overrideProviders( TestProvidersComp, - [provide(FancyService, {useClass: MockFancyService})] + [{ provide: FancyService, useClass: MockFancyService }] ) .createAsync(TestProvidersComp) .then(fixture => { @@ -329,7 +328,7 @@ describe('test component builder', function() { tcb.overrideViewProviders( TestViewProvidersComp, - [provide(FancyService, {useClass: MockFancyService})] + [{ provide: FancyService, useClass: MockFancyService }] ) .createAsync(TestViewProvidersComp) .then(fixture => { @@ -495,7 +494,7 @@ describe('tcb.overrideProviders', () => { tcb.overrideProviders( AnotherProvidersComp, - [provide(HeroService, {useValue: {}})] + [{ provide: HeroService, useValue: {}} ] ) .createAsync(AnotherProvidersComp); }))); diff --git a/public/docs/_examples/testing/ts/app/bag.ts b/public/docs/_examples/testing/ts/app/bag.ts index 0da4d7fb20..94be448e7e 100644 --- a/public/docs/_examples/testing/ts/app/bag.ts +++ b/public/docs/_examples/testing/ts/app/bag.ts @@ -1,6 +1,6 @@ // Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts /* tslint:disable:forin */ -import { Component, EventEmitter, Injectable, Input, Output, +import { Component, EventEmitter, Injectable, Input, Output, Optional, OnInit, OnChanges, OnDestroy, SimpleChange } from '@angular/core'; import { Observable } from 'rxjs/Rx'; @@ -13,6 +13,8 @@ import { Observable } from 'rxjs/Rx'; export class FancyService { value: string = 'real value'; + getValue() { return this.value; } + getAsyncValue() { return Promise.resolve('async value'); } getObservableValue() { return Observable.of('observable value'); } @@ -123,20 +125,36 @@ export class TestViewProvidersComp { constructor(private fancyService: FancyService) {} } - @Component({ moduleId: module.id, selector: 'external-template-comp', templateUrl: 'bag-external-template.html' }) -export class ExternalTemplateComp { } +export class ExternalTemplateComp { + serviceValue: string; + constructor(@Optional() private service: FancyService) { } + + ngOnInit() { + if (this.service) { this.serviceValue = this.service.getValue(); } + } +} + +@Component({ + selector: 'comp-w-ext-comp', + template: ` +

comp-w-ext-comp

+ + `, + directives: [ExternalTemplateComp] +}) +export class CompWithCompWithExternalTemplate { } @Component({ selector: 'bad-template-comp', templateUrl: 'non-existant.html' }) -export class BadTemplateUrl { } +export class BadTemplateUrlComp { } ///////// MyIfChildComp //////// @@ -222,3 +240,16 @@ export class MyIfParentComp implements OnInit { this.toggleLabel = this.showChild ? 'Close' : 'Show'; } } + +export const BAG_PROVIDERS = [FancyService]; + +export const BAG_DIRECTIVES = [ + ButtonComp, + ChildChildComp, ChildComp, ChildWithChildComp, + ExternalTemplateComp, CompWithCompWithExternalTemplate, + InputComp, + MyIfComp, MyIfChildComp, MyIfParentComp, + MockChildComp, MockChildChildComp, + ParentComp, + TestProvidersComp, TestViewProvidersComp +]; diff --git a/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts b/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts index 5dd61bc8f3..803ea00535 100644 --- a/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts @@ -2,7 +2,6 @@ import { DashboardComponent } from './dashboard.component'; import { By } from '@angular/platform-browser'; -import { provide } from '@angular/core'; import { beforeEach, beforeEachProviders, @@ -74,9 +73,9 @@ describe('DashboardComponent', () => { beforeEachProviders(() => { mockHeroService = new MockHeroService(); return [ - provide(Router, {useClass: MockRouter}), - provide(MockRouter, {useExisting: Router}), - provide(HeroService, {useValue: mockHeroService}) + { provide: Router, useClass: MockRouter}, + { provide: MockRouter, useExisting: Router}, + { provide: HeroService, useValue: mockHeroService } ]; }); diff --git a/public/docs/_examples/testing/ts/app/expect-proper.ts b/public/docs/_examples/testing/ts/app/expect-proper.ts new file mode 100644 index 0000000000..6712b879d1 --- /dev/null +++ b/public/docs/_examples/testing/ts/app/expect-proper.ts @@ -0,0 +1,9 @@ +// See https://github.com/angular/angular/issues/9017 +import { expect as expectCore} from '@angular/core/testing'; +import { NgMatchers } from '@angular/platform-browser/testing'; + +export function expect(spy: Function): NgMatchers; +export function expect(actual: any): NgMatchers; +export function expect(actual: any): NgMatchers { + return expectCore(actual) as NgMatchers; +} diff --git a/public/docs/_examples/testing/ts/app/hero-detail.component.ts b/public/docs/_examples/testing/ts/app/hero-detail.component.ts index 3e1eaf4cd8..5cc03cff4b 100644 --- a/public/docs/_examples/testing/ts/app/hero-detail.component.ts +++ b/public/docs/_examples/testing/ts/app/hero-detail.component.ts @@ -5,7 +5,7 @@ import { Component, OnInit } from '@angular/core'; // #enddocregion import-oninit // #docregion import-route-params -import {RouteParams} from '@angular/router-deprecated'; +import { RouteParams } from '@angular/router-deprecated'; // #enddocregion import-route-params import { Hero } from './hero'; diff --git a/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts b/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts index c80758e680..0c79f7f3aa 100644 --- a/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts +++ b/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts @@ -8,8 +8,6 @@ import { import { TestComponentBuilder } from '@angular/compiler/testing'; -import { provide } from '@angular/core'; - import { MockBackend, MockConnection } from '@angular/http/testing'; @@ -46,7 +44,7 @@ describe('Http-HeroService (mockBackend)', () => { beforeEachProviders(() => [ HTTP_PROVIDERS, - provide(XHRBackend, {useClass: MockBackend}) + { provide: XHRBackend, useClass: MockBackend } ]); it('can instantiate service when inject service', diff --git a/public/docs/_examples/testing/ts/app/main.ts b/public/docs/_examples/testing/ts/app/main.ts index 33b9c2b641..5f185788a3 100644 --- a/public/docs/_examples/testing/ts/app/main.ts +++ b/public/docs/_examples/testing/ts/app/main.ts @@ -1,6 +1,5 @@ import { bootstrap } from '@angular/platform-browser-dynamic'; import { AppComponent } from './app.component'; -import { MyIfParentComp } from './bag'; bootstrap(AppComponent); -bootstrap(MyIfParentComp); + diff --git a/public/docs/_examples/testing/ts/app/my-uppercase.pipe.1.ts b/public/docs/_examples/testing/ts/app/my-uppercase.pipe.1.ts index 9702bec94b..94b5bc45ce 100644 --- a/public/docs/_examples/testing/ts/app/my-uppercase.pipe.1.ts +++ b/public/docs/_examples/testing/ts/app/my-uppercase.pipe.1.ts @@ -1,5 +1,5 @@ // #docregion -import {Pipe, PipeTransform} from '@angular/core'; +import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'my-uppercase' }) export class MyUppercasePipe implements PipeTransform { diff --git a/public/docs/_examples/testing/ts/app/my-uppercase.pipe.ts b/public/docs/_examples/testing/ts/app/my-uppercase.pipe.ts index 0d90d14284..6584f92ef6 100644 --- a/public/docs/_examples/testing/ts/app/my-uppercase.pipe.ts +++ b/public/docs/_examples/testing/ts/app/my-uppercase.pipe.ts @@ -1,6 +1,6 @@ // #docregion // #docregion depends-on-angular -import {Pipe, PipeTransform} from '@angular/core'; +import { Pipe, PipeTransform } from '@angular/core'; // #enddocregion depends-on-angular @Pipe({ name: 'my-uppercase' }) diff --git a/public/docs/_examples/testing/ts/index.html b/public/docs/_examples/testing/ts/index.html index 02b11078c5..bfde80afe3 100644 --- a/public/docs/_examples/testing/ts/index.html +++ b/public/docs/_examples/testing/ts/index.html @@ -22,7 +22,5 @@ Loading... -
- Loading MyIfParentComp ... diff --git a/public/docs/_examples/toh-6/ts/app/main.ts b/public/docs/_examples/toh-6/ts/app/main.ts index be838f0855..958b9a8c69 100644 --- a/public/docs/_examples/toh-6/ts/app/main.ts +++ b/public/docs/_examples/toh-6/ts/app/main.ts @@ -1,7 +1,6 @@ // #docplaster // #docregion final // Imports for loading & configuring the in-memory web api -import { provide } from '@angular/core'; import { XHRBackend } from '@angular/http'; import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api'; @@ -23,7 +22,7 @@ bootstrap(AppComponent, [ HTTP_PROVIDERS ]); // #docregion final bootstrap(AppComponent, [ HTTP_PROVIDERS, - provide(XHRBackend, { useClass: InMemoryBackendService }), // in-mem server - provide(SEED_DATA, { useClass: InMemoryDataService }) // in-mem server data + { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server + { provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data ]); // #enddocregion final diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.spec.ts b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.spec.ts index c44f82b4fe..9fe842a4e4 100644 --- a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.spec.ts +++ b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.spec.ts @@ -1,5 +1,4 @@ // #docregion -import { provide } from '@angular/core'; import { describe, beforeEach, @@ -29,11 +28,11 @@ describe('Phone', function() { Phone, MockBackend, BaseRequestOptions, - provide(Http, { + { provide: Http, useFactory: (backend: MockBackend, options: BaseRequestOptions) => new Http(backend, options), deps: [MockBackend, BaseRequestOptions] - }) + } ]); beforeEach(inject([MockBackend, Phone], diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.spec.ts b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.spec.ts index 65abca7c8d..67809b486a 100644 --- a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.spec.ts +++ b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.spec.ts @@ -1,5 +1,4 @@ // #docregion -import { provide } from '@angular/core'; import { HTTP_PROVIDERS } from '@angular/http'; import { Observable } from 'rxjs/Rx'; @@ -35,8 +34,8 @@ class MockPhone extends Phone { describe('PhoneDetailComponent', () => { beforeEachProviders(() => [ - provide(Phone, {useClass: MockPhone}), - provide('$routeParams', {useValue: {phoneId: 'xyz'}}), + { provide: Phone, useClass: MockPhone }, + { provide: '$routeParams', useValue: {phoneId: 'xyz'}}, HTTP_PROVIDERS ]); diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.spec.ts b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.spec.ts index 4b96534b43..d050615923 100644 --- a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.spec.ts +++ b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.spec.ts @@ -1,5 +1,4 @@ // #docregion -import { provide } from '@angular/core'; import { HTTP_PROVIDERS } from '@angular/http'; import { Observable } from 'rxjs/Rx'; import { @@ -32,7 +31,7 @@ class MockPhone extends Phone { describe('PhoneList', () => { beforeEachProviders(() => [ - provide(Phone, {useClass: MockPhone}), + { provide: Phone, useClass: MockPhone }, HTTP_PROVIDERS ]); diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/phone.service.spec.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/phone.service.spec.ts index 54521ddfc5..ab664dc4c8 100644 --- a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/phone.service.spec.ts +++ b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/phone.service.spec.ts @@ -1,4 +1,3 @@ -import { provide } from '@angular/core'; import { describe, beforeEach, @@ -23,15 +22,15 @@ describe('Phone', function() { {name: 'Phone Z', snippet: '', images: []} ]; let mockBackend:MockBackend; - + beforeEachProviders(() => [ Phone, MockBackend, BaseRequestOptions, - provide(Http, { + { provide: Http, useFactory: (backend: MockBackend, options: BaseRequestOptions) => new Http(backend, options), deps: [MockBackend, BaseRequestOptions] - }) + } ]); beforeEach(inject([MockBackend, Phone], (_mockBackend_:MockBackend, _phone_:Phone) => { diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/main.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/main.ts index 48edd80f40..f59e9c7afd 100644 --- a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/main.ts +++ b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/main.ts @@ -1,6 +1,5 @@ // #docregion // #docregion imports -import { provide } from '@angular/core'; import { LocationStrategy, HashLocationStrategy, @@ -17,8 +16,8 @@ import { AppComponent } from './app.component'; bootstrap(AppComponent, [ HTTP_PROVIDERS, ROUTER_PROVIDERS, - provide(APP_BASE_HREF, {useValue: '!'}), - provide(LocationStrategy, {useClass: HashLocationStrategy}), + { provide: APP_BASE_HREF, useValue: '!' }, + { provide: LocationStrategy, useClass: HashLocationStrategy }, Phone ]); // #enddocregion bootstrap diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts index 32e86a2134..c553df3d29 100644 --- a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts +++ b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts @@ -1,5 +1,4 @@ // #docregion -import { provide } from '@angular/core'; import { HTTP_PROVIDERS } from '@angular/http'; // #docregion routeparams import { RouteParams } from '@angular/router-deprecated'; @@ -41,8 +40,8 @@ describe('PhoneDetailComponent', () => { // #docregion routeparams beforeEachProviders(() => [ - provide(Phone, {useClass: MockPhone}), - provide(RouteParams, {useValue: new RouteParams({phoneId: 'xyz'})}), + { provide: Phone, useClass: MockPhone }, + { provide: RouteParams, useValue: new RouteParams({phoneId: 'xyz'})}, HTTP_PROVIDERS ]); // #enddocregion routeparams diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.spec.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.spec.ts index e8949d9463..bfdde7e1fc 100644 --- a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.spec.ts +++ b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.spec.ts @@ -1,5 +1,5 @@ // #docregion routestuff -import { provide, ApplicationRef } from '@angular/core'; +import { ApplicationRef } from '@angular/core'; import { LocationStrategy, HashLocationStrategy } from '@angular/common'; import { HTTP_PROVIDERS } from '@angular/http'; import { @@ -40,14 +40,14 @@ class MockPhone extends Phone { describe('PhoneList', () => { // #docregion routestuff - + beforeEachProviders(() => [ - provide(Phone, {useClass: MockPhone}), + { provide: Phone, useClass: MockPhone}, HTTP_PROVIDERS, ROUTER_PROVIDERS, - provide(ApplicationRef, {useClass: MockApplicationRef}), - provide(ROUTER_PRIMARY_COMPONENT, {useValue: AppComponent}), - provide(LocationStrategy, {useClass: MockLocationStrategy}), + { provide: ApplicationRef, useClass: MockApplicationRef }, + { provide: ROUTER_PRIMARY_COMPONENT, useValue: AppComponent }, + { provide: LocationStrategy, useClass: MockLocationStrategy}, ]); // #enddocregion routestuff diff --git a/public/docs/_examples/webpack/ts/config/karma-test-shim.js b/public/docs/_examples/webpack/ts/config/karma-test-shim.js index 67a2409917..27fa731f5a 100644 --- a/public/docs/_examples/webpack/ts/config/karma-test-shim.js +++ b/public/docs/_examples/webpack/ts/config/karma-test-shim.js @@ -8,6 +8,7 @@ require('zone.js/dist/zone'); require('zone.js/dist/long-stack-trace-zone'); require('zone.js/dist/jasmine-patch'); require('zone.js/dist/async-test'); +require('zone.js/dist/fake-async-test'); var appContext = require.context('../src', true, /\.spec\.ts/); diff --git a/public/docs/dart/latest/guide/dependency-injection.jade b/public/docs/dart/latest/guide/dependency-injection.jade index b6666929c3..0f991c5673 100644 --- a/public/docs/dart/latest/guide/dependency-injection.jade +++ b/public/docs/dart/latest/guide/dependency-injection.jade @@ -46,9 +46,6 @@ block real-logger block optional-logger //- TBC. -block provider-function-etc - //- N/A - block provider-ctor-args - var _secondParam = 'named parameter, such as useClass' :marked @@ -62,10 +59,8 @@ block dart-diff-const-metadata For that reason, we can't call functions to get values to use within an annotation. Instead, we use constant literals or constant constructors. - For example, a TypeScript program might use the - function call `provide(Logger, {useClass: BetterLogger})`, - which is equivalent to the TypeScript code - `new Provider(Logger, {useClass: BetterLogger})`. + For example, a TypeScript program will use the + object literal `{ provide: Logger, useClass: BetterLogger }`. A Dart annotation would instead use the constant value `const Provider(Logger, useClass: BetterLogger)`. block dart-diff-const-metadata-ctor diff --git a/public/docs/ts/latest/cookbook/dependency-injection.jade b/public/docs/ts/latest/cookbook/dependency-injection.jade index acef54362b..8da5d19fbf 100644 --- a/public/docs/ts/latest/cookbook/dependency-injection.jade +++ b/public/docs/ts/latest/cookbook/dependency-injection.jade @@ -5,160 +5,160 @@ include ../_util-fns In this cookbook we will explore many of the features of Dependency Injection (DI) in Angular. 依赖注入是一个用来管理代码依赖的强大模式。在这本“烹饪宝典”中,我们会讨论Angular依赖注入的许多特性。 - + :marked ## Table of contents - + ## 目录 [Application-wide dependencies](#app-wide-dependencies) - + [应用程序全局依赖](#app-wide-dependencies) [External module configuration](#external-module-configuration) - + [外部模块配置](#external-module-configuration) - + [*@Injectable* and nested service dependencies](#nested-dependencies) - - [*@Injectable*与嵌套服务的依赖](#nested-dependencies) + + [*@Injectable*与嵌套服务的依赖](#nested-dependencies) [Limit service scope to a component subtree](#service-scope) - + [把服务作用范围限制到一个子组件树](#service-scope) - + [Multiple service instances (sandboxing)](#multiple-service-instances) - + [多个服务实例(沙箱)](#multiple-service-instances) - + [Qualify dependency lookup with *@Optional* and *@Host*](#qualify-dependency-lookup) - + [使用*@Optional*和*@Host*装饰器来限定依赖查找方式](#qualify-dependency-lookup) - + [Inject the component's DOM element](#component-element) - + [注入组件的DOM元素](#component-element) - + [Define dependencies with providers](#providers) - + [使用供应商定义依赖](#providers) - - * [The *provide* function](#provide) - - * [*provide*函数](#provide) - - * [useValue - the *value provider*](#usevalue) - - * [useValue - *值供应商*](#usevalue) - - * [useClass - the *class provider*](#useclass) - - * [useClass - *类供应商*](#useclass) - + + * [The *provide* Object literal](#provide) + + * [*provide*对象](#provide) + + * [useValue - the *value provider*](#usevalue) + + * [useValue - *值供应商*](#usevalue) + + * [useClass - the *class provider*](#useclass) + + * [useClass - *类供应商*](#useclass) + * [useExisting - the *alias provider*](#useexisting) * [useExisting - *别名供应商*](#useexisting) - - * [useFactory - the *factory provider*](#usefactory) - - * [useFactory - *工厂供应商*](#usefactory) + + * [useFactory - the *factory provider*](#usefactory) + + * [useFactory - *工厂供应商*](#usefactory) [Define providers with object literals](#object-literals) [使用对象文本定义供应商] (#object-literals) - + [Provider token alternatives](#tokens) - + [供应商可选令牌](#tokens) - + * [class-interface](#class-interface) - + * [类-接口](#class-interface) - + * [OpaqueToken](#opaque-token) - * [Opaque令牌](#opaque-token) - + * [Opaque令牌](#opaque-token) + [Inject into a derived class](#di-inheritance) [注入到一个派生类](#di-inheritance) - + [Find a parent component by injection](#find-parent) - + [通过注入来查找父组件](#find-parent) - + * [Find parent with a known component type](#known-parent) - + * [通过已知组件类型查找父组件](#known-parent) - + * [Cannot find a parent by its base class](#base-parent) - + * [无法通过自己的基类查找父组件](#base-parent) - + * [Find a parent by its class-interface](#class-interface-parent) - + * [通过类-接口查找父组件](#class-interface-parent) - + * [Find a parent in a tree of parents (*@SkipSelf*)](#parent-tree) - + * [在父组件树里查找一个父组件(*@SkipSelf*)](#parent-tree) - + * [A *provideParent* helper function](#provideparent) - + * [*provideParent*助手函数](#provideparent) - + [Break circularities with a forward class reference (*forwardRef*)](#forwardref) [使用类的前向引用(*forwardRef*)打破循环依赖](#forwardref) - + :marked **See the [live example](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)** of the code supporting this cookbook. 要获取本“烹饪宝典”的代码,**参见[在线例子](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)**。 - + .l-main-section :marked ## Application-wide dependencies - + ## 应用程序全局依赖 - + Register providers for dependencies used throughout the application in the root application component, `AppComponent`. 在应用程序根组件`AppComponent`中注册那些被应用程序全局使用的依赖供应商。 - - In the following example, we import and register several services + + In the following example, we import and register several services (the `LoggerService`, `UserContext`, and the `UserService`) in the `@Component` metadata `providers` array. - + 在下面的例子中,通过`@Component`元数据的`providers`数组导入和注册了几个服务(`LoggerService`, `UserContext`和`UserService`)。 - + +makeExample('cb-dependency-injection/ts/app/app.component.ts','import-services','app/app.component.ts (excerpt)')(format='.') :marked All of these services are implemented as classes. Service classes can act as their own providers which is why listing them in the `providers` array is all the registration we need. - + 所有这些服务都是用类实现的。服务类能充当自己的供应商,这就是为什么只要把它们列在`providers`数组里就算注册成功了。 - + .l-sub-section :marked A *provider* is something that can create or deliver a service. Angular creates a service instance from a class provider by "new-ing" it. Learn more about Providers [below](#providers). - + *供应商*是用来新建或者交付服务的。Angular拿到“类供应商”之后,会通过“new”操作来新建服务实例。从[下面](#providers)可以学到更多关于供应商的知识。 - + :marked - Now that we've registered these services, + Now that we've registered these services, Angular can inject them into the constructor of *any* component or service, *anywhere* in the application. - + 现在我们已经注册了这些服务,这样Angular就能在应用程序的*任何地方*,把它们注入到*任何*组件和服务的构造函数里。 - -+makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','ctor','app/hero-bios.component.ts (component constructor injection)')(format='.') + ++makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','ctor','app/hero-bios.component.ts (component constructor injection)')(format='.') +makeExample('cb-dependency-injection/ts/app/user-context.service.ts','ctor','app/user-context.service.ts (service constructor injection)')(format='.') @@ -167,70 +167,70 @@ include ../_util-fns :marked ## External module configuration ## 外部模块设置 - - We can register _certain_ module providers when bootstrapping rather than in the root application component. + + We can register _certain_ module providers when bootstrapping rather than in the root application component. 也可以在引导过程中注册_某些_模块供应商,而非在应用程序根组件里。 - + We'd do this when we expect to select or configure external modules that support our application but (a) aren't conceptually part of the application and (b) that we could change later without altering the essential logic of the application. 使用外部模块时,如果满足下面两个条件,就应该在引导过程中注册:a)它在概念上不是我们程序的一部分,以及 b)将来我们可能要在不改变主要应用逻辑的情况下更换它。 - - For example, we might configure the Component Router with different + + For example, we might configure the Component Router with different [location strategies](../guide/router.html#location-strategy) based on environmental factors. The choice of location strategy doesn't matter to the application itself. 比如,可能会根据不同的环境组件路由器,使用不同的[location策略](../guide/router.html#location-strategy)来配置组件路由器。而这个location策略不会直接影响到应用程序本身。 - - We could sneak in a fake HTTP backend with sample data during development rather than + + We could sneak in a fake HTTP backend with sample data during development rather than allow http calls to a remote server (that might not yet exist). We'll switch to the real backend in production. The application shouldn't know or care one way or the other. 在开发过程中,可以偷偷把一个假的带样本数据的HTTP后端嵌入进来,以代替对一个远程服务器(可能还不存在)进行http查询。我们在产品发布时再切换到真正的后端。应用程序不用知道也不用管正在跟哪个后端打交道。 - + See both examples in the following `main.ts` where we list their service providers in an array in the second parameter of the `bootstrap` method. 在下面`main.ts`的两个例子中,在`bootstrap`方法的第二个数组型参数中,我们列出了它们的服务供应商。 - -+makeExample('cb-dependency-injection/ts/app/main.ts','bootstrap','app/main.ts')(format='.') + ++makeExample('cb-dependency-injection/ts/app/main.ts','bootstrap','app/main.ts')(format='.') a(id="injectable") a(id="nested-dependencies") .l-main-section :marked ## *@Injectable* and nested service dependencies - + ## *@Injectable*和嵌套服务依赖 - + 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. 这些被注入服务的消费者不需要知道如何创建这个服务,它也不应该在乎。新建和缓存这个服务是依赖注入器的工作。 - + 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. 有时候一个服务依赖其它服务...而其它服务可能依赖另外的更多服务。按正确的顺序解析这些嵌套依赖也是框架的工作。 在每一步,依赖的使用者只要在它的构造函数里简单声明它需要什么,框架就会完成所有剩下的事情。 - - For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`. - + + For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`. + 比如,我们在`AppComponent`里注入的`LoggerService`和`UserContext`。 - -+makeExample('cb-dependency-injection/ts/app/app.component.ts','ctor','app/app.component.ts')(format='.') + ++makeExample('cb-dependency-injection/ts/app/app.component.ts','ctor','app/app.component.ts')(format='.') :marked The `UserContext` in turn has dependencies on both the `LoggerService` (again) and a `UserService` that gathers information about a particular user. `UserContext`有两个依赖`LoggerService`(再一次)和负责获取特定用户信息的`UserService`。 - + +makeExample('cb-dependency-injection/ts/app/user-context.service.ts','injectables','user-context.service.ts (injection)')(format='.') :marked @@ -242,42 +242,42 @@ a(id="nested-dependencies") 当Angular新建`AppComponent`时,依赖注入框架先创建一个`LoggerService`的实例,然后创建`UserContextService`实例。 `UserContextService`需要框架已经创建好的`LoggerService`实例和尚未创建的`UserService`实例。 `UserService`没有其它依赖,所以依赖注入框架可以直接`new`一个实例。 - - The beauty of dependency injection is that the author of `AppComponent` didn't care about any of this. + + The beauty of dependency injection is that the author of `AppComponent` didn't care about any of this. The author simply declared what was needed in the constructor (`LoggerService` and `UserContextService`) and the framework did the rest. 依赖注入最帅的地方在于,`AppComponent`的作者不需要在乎这一切。作者只是在(`LoggerService`和`UserContextService`的)构造函数里面简单的声明一下,框架就完成了剩下的工作。 - + Once all the dependencies are in place, the `AppComponent` displays the user information: - + 一旦所有依赖都准备好了,`AppComponent`就会显示用户信息: - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User") :marked ### *@Injectable()* ### *@Injectable()* - - Notice the `@Injectable()`decorator on the `UserContextService` class. - + + Notice the `@Injectable()`decorator on the `UserContextService` class. + 注意在`UserContextService`类里面的`@Injectable()`装饰器。 - + +makeExample('cb-dependency-injection/ts/app/user-context.service.ts','injectable','user-context.service.ts (@Injectable)')(format='.') :marked That decorator makes it possible for Angular to identify the types of its two dependencies, `LoggerService` and `UserService`. 该装饰器让Angular有能力识别这两个依赖 `LoggerService` 和 `UserService`的类型。 - + Technically, the `@Injectable()`decorator is only _required_ for a service class that has _its own dependencies_. The `LoggerService` doesn't depend on anything. The logger would work if we omitted `@Injectable()` and the generated code would be slightly smaller. 从技术上讲,这个`@Injectable()`装饰器只在一个服务类有_自己的依赖_的时候,才是_不可缺少_的。 `LoggerService`不依赖任何东西,所以该日志服务在没有`@Injectable()`的时候应该也能工作,生成的代码也更少一些。 - + But the service would break the moment we gave it a dependency and we'd have to go back and and add `@Injectable()` to fix it. We add `@Injectable()` from the start for the sake of consistency and to avoid future pain. - + 但是在给它添加依赖的那一瞬间,该服务就会停止工作,要想修复它,就必须要添加`@Injectable()`。 为了保持一致性和防止将来的麻烦,推荐从一开始就加上`@Injectable()`。 @@ -285,7 +285,7 @@ figure.image-display :marked Although we recommend applying `@Injectable` to all service classes, do not feel bound by it. Some developers prefer to add it only where needed and that's a reasonable policy too. - + 虽然我们推荐在所有服务中使用`@Injectable()`,但你也不需要一定要这么做。一些开发者就更喜欢在真正需要的地方才添加,这也是一个合理的策略。 .l-sub-section @@ -293,68 +293,68 @@ figure.image-display The `AppComponent` class had two dependencies as well but no `@Injectable()`. It didn't need `@Injectable()` because that component class has the `@Component` decorator. In Angular with TypeScript, a *single* decorator — *any* decorator — is sufficient to identify dependency types. - + `AppComponent`类有两个依赖,但它没有`@Injectable()`。 它不需要`@Injectable()`,这是因为组件类有`@Component`装饰器。 在用TypeScript的Angular应用里,有一个*单独的*装饰器 — *任何*装饰器 — 来标识依赖的类型就够了。 - + .l-main-section :marked ## Limit service scope to a component subtree ## 把服务作用范围限制到一个组件支树 - - All injected service dependencies are singletons meaning that, + + All injected service dependencies are singletons meaning that, for a given dependency injector ("injector"), there is only one instance of service. - + 所有被注入的服务依赖都是单例的,也就是说,在任意一个依赖注入器("injector")中,每个服务只有唯一的实例。 - + 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. - + 但是Angular应用程序有多个依赖注入器,组织成一个与组件树平行的树状结构。所以,可以在任何组件级别*提供*(和建立)特定的服务。如果在多个组件中注入,服务就会被新建出多个实例,分别提供给不同的组件。 - + 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. 默认情况下,一个组件中注入的服务依赖,会在该组件的所有子组件中可见,而且Angular会把同样的服务实例注入到需要该服务的子组件中。 - + Accordingly, dependencies provided in the root `AppComponent` can be injected into *any* component *anywhere* in the application. 所以,在根部的`AppComponent`提供的依赖单例就能被注入到应用程序中*任何地方*的*任何*组件。 - - That isn't always desirable. + + That isn't always desirable. Sometimes we want to restrict service availability to a particular region of the application. 但这不一定总是我们想要的。有时候我们想要把服务的有效性限制到应用程序的一个特定区域。 - + We 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*. Here we provide the `HeroService` to the `HeroesBaseComponent` by listing it in the `providers` array: - + 通过*在组件树的子级根组件*中提供服务,可以把一个被注入服务的作用范围局限在应用程序结构中的某个*分支*中。 这里通过列入`providers`数组,在`HeroesBaseComponent`中提供了`HeroService`: - + +makeExample('cb-dependency-injection/ts/app/sorted-heroes.component.ts','injection','app/sorted-heroes.component.ts (HeroesBaseComponent excerpt)') :marked 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). 当Angular新建`HeroBaseComponent`的时候,它会同时新建一个`HeroService`实例,该实例只在该组件及其子组件(如果有)中可见。 - - We could also provide the `HeroService` to a *different* component elsewhere in the application. + + We 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. - + 我们也可以在应用程序别处的*不同的*组件里提供`HeroService`。这样就会导致在*不同*注入器中存在该服务的*不同*实例。 - + .l-sub-section :marked We examples of such scoped `HeroService` singletons appear throughout the accompanying sample code, including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`. Each of these components has its own `HeroService` instance managing its own independent collection of heroes. - + 这个例子中,局部化的`HeroService`单例,遍布整份范例代码,包括`HeroBiosComponent`、`HeroOfTheMonthComponent`和`HeroBaseComponent`。 这些组件每个都有自己的`HeroService`实例,用来管理独立的英雄库。 @@ -362,25 +362,25 @@ figure.image-display .alert.is-helpful :marked ### 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. 对一些Angular开发者来说,这么多依赖注入知识可能已经是他们需要知道的全部了。不是每个人都需要更复杂的用法。 - + .l-main-section :marked ## Multiple service instances (sandboxing) ## 多个服务实例(sandboxing) - + Sometimes we 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. + + A good example is a service that holds state for its companion component instance. We need a separate instance of the service for each component. Each service has its own work-state, isolated from the service-and-state of a different component. We call this *sandboxing* because each service and component instance has its own sandbox to play in. @@ -388,32 +388,32 @@ figure.image-display 一个用来保存其伴生组件的实例状态的服务就是个好例子。 对每个组件,我们都需要该服务的单独实例。 每个服务有自己的工作状态,与其它组件的服务和状态隔离。我们称作*沙盒化*,因为每个服务和组件实例都在自己的沙盒里运行。 - + Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`. - + 想象一下,一个`HeroBioComponent`组件显示三个`HeroBioComponent`的实例。 - + +makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','simple','ap/hero-bios.component.ts') :marked 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`都能编辑一个英雄的生平。`HeroBioComponent`依赖`HeroCacheService`服务来对该英雄进行读取、缓存和执行其它持久化操作。 - -+makeExample('cb-dependency-injection/ts/app/hero-cache.service.ts','service','app/hero-cache.service.ts') + ++makeExample('cb-dependency-injection/ts/app/hero-cache.service.ts','service','app/hero-cache.service.ts') :marked 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. 很明显,这三个`HeroBioComponent`实例不能共享一样的`HeroCacheService`。要不然它们会相互冲突,争相把自己的英雄放在缓存里面。 - - Each `HeroBioComponent` gets its *own* `HeroCacheService` instance + + Each `HeroBioComponent` gets its *own* `HeroCacheService` instance by listing the `HeroCacheService` in its metadata `providers` array. - + 通过在自己的元数据(metadata)`providers`数组里面列出`HeroCacheService`, 每个`HeroBioComponent`就能*拥有*自己独立的`HeroCacheService`实例。 - -+makeExample('cb-dependency-injection/ts/app/hero-bio.component.ts','component','app/hero-bio.component.ts') + ++makeExample('cb-dependency-injection/ts/app/hero-bio.component.ts','component','app/hero-bio.component.ts') :marked The parent `HeroBiosComponent` binds a value to the `heroId`. The `ngOnInit` pass that `id` to the service which fetches and caches the hero. @@ -421,12 +421,12 @@ figure.image-display And the template displays this data-bound property. 父组件`HeroBioComponent`把一个值绑定到`heroId`。`ngOnInit`把该`id`传递到服务,然后服务获取和缓存英雄。`hero`属性的getter从服务里面获取缓存的英雄,并在模板里显示它绑定到属性值。 - + Find this example in [live code](/resources/live-examples/cb-dependency-injection/ts/plnkr.html) and confirm that the three `HeroBioComponent` instances have their own cached hero data. - + 到[在线代码](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)找到这个例子,确认三个`HeroBioComponent`实例拥有自己独立的英雄数据缓存。 - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios") @@ -435,18 +435,18 @@ a(id="qualify-dependency-lookup") .l-main-section :marked ## Qualify dependency lookup with *@Optional* and *@Host* - + ## 使用*@Optional*和*@Host*装饰器来限定依赖查找方式 - - We learned that dependencies can be registered at any level in the component hierarchy. + + We learned that 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 + + 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. - + 当组件申请一个依赖时,Angular从该组件本身的注入器开始,沿着依赖注入器的树往上找,直到找到第一个符合要求的供应商。如果Angular不能在这个过程中找到合适的依赖,它就会抛出一个错误。 - + We *want* this behavior most of the time. But sometimes we need to limit the search and/or accommodate a missing dependency. We can modify Angular's search behavior with the `@Host` and `@Optional` qualifying decorators, @@ -455,63 +455,63 @@ a(id="qualify-dependency-lookup") 大部分时候,我们确实*想要*这个行为。 但是有时候,我们需要限制这个(依赖)查找逻辑,且/或提供一个缺失的依赖。 单独或联合使用`@Host`和`@Optional`限定型装饰器,我们就可以修改Angular的查找行为。 - - The `@Optional` decorator tells Angular to continue when it can't find the dependency. + + The `@Optional` decorator tells Angular to continue when it can't find the dependency. Angular sets the injection parameter to `null` instead. 当Angular找不到依赖时,`@Optional`装饰器会告诉Angular继续执行。Angular把此注入参数设置为`null`(而不用默认的抛出错误的行为)。 - - The `@Host` decorator stops the upward search at the *host component*. + + The `@Host` decorator stops the upward search at the *host component*. `@Host`装饰器将把往上搜索的行为截止在*宿主组件* - - The host component is typically the component requesting the dependency. + + 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. We look at this second, more interesting case in our next example. 宿主组件通常是申请这个依赖的组件。但当这个组件被投影(projected)进一个*父组件*后,这个父组件就变成了宿主。我们先思考一下,更多有趣的案例还在后面。 - + ### Demonstration ### 示范 - + The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](#hero-bios-component). - + `HeroBiosAndContactsComponent`是[前面](#hero-bios-component)我们见过的`HeroBiosComponent`的修改版。 - + +makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','hero-bios-and-contacts','app/hero-bios.component.ts (HeroBiosAndContactsComponent)') :marked Focus on the template: - + 注意看模板: - + +makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','template')(format='.') :marked We've inserted a `` element between the `` tags. Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view, placing it in the `` slot of the `HeroBioComponent` template: - + 我们在``标签中插入了``元素。Angular就会把相应的`HeroContactComponent`*投影*(*transclude*)进`HeroBioComponent`的视图里, 将它放在`HeroBioComponent`模板的``标签槽里。 - + +makeExample('cb-dependency-injection/ts/app/hero-bio.component.ts','template','app/hero-bio.component.ts (template)')(format='.') :marked It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description: - + 从`HeroContactComponent`获得的英雄电话号码,被投影到上面的英雄描述里,看起来像这样: - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact") :marked Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section: - + 下面的`HeroContactComponent`,示范了我们在本节一直在讨论的限定型装饰器(@Optional和@Host): - + +makeExample('cb-dependency-injection/ts/app/hero-contact.component.ts','component','app/hero-contact.component.ts') :marked Focus on the constructor parameters - + 注意看构造函数的参数 - + +makeExample('cb-dependency-injection/ts/app/hero-contact.component.ts','ctor-params','app/hero-contact.component.ts')(format='.') :marked The `@Host()` function decorating the `heroCache` property ensures that @@ -519,30 +519,30 @@ figure.image-display Angular throws if the parent lacks that service, even if a component higher in the component tree happens to have that service. `@Host()`函数是`heroCache`属性的装饰器,确保我们从其父组件`HeroBioComponent`得到一个缓存服务。如果该父组件不存在这个服务,Angular就会抛出错误,即使组件树里的再上级有某个组件拥有这个服务,Angular也会抛出错误。 - + A second `@Host()` function decorates the `loggerService` property. We know the only `LoggerService` instance in the app is provided at the `AppComponent` level. The host `HeroBioComponent` doesn't have its own `LoggerService` provider. 另一个`@Host()`函数是属性`loggerService`的装饰器,我们知道在应用程序中,只有一个`LoggerService`实例,也就是在`AppComponent`级提供的服务。 该宿主`HeroBioComponent`没有自己的`LoggerService`供应商。 - + Angular would throw an error if we 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. 如果我们没有同时使用`@Optional()`装饰器的话,Angular就会抛出错误。多亏了`@Optional()`,Angular把`loggerService`设置为null,并继续执行组件而不会抛出错误。 - + .l-sub-section :marked We'll come back to the `elementRef` property shortly. - + 我们将很快回到`elementRef`属性。 - + :marked Here's the `HeroBiosAndContactsComponent` in action. - + 下面是`HeroBiosAndContactsComponent`的执行结果: - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into") :marked @@ -558,7 +558,7 @@ figure.image-display the application fails for lack of the required logger at the host component level.
`EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)` - + 另一方面,如果我们恢复`@Host()`装饰器,注释掉`@Optional`,应用程序就会运行失败,因为它在宿主组件级别找不到需要的日志服务。
`EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)` @@ -568,42 +568,42 @@ figure.image-display ## Inject the component's element ## 注入组件的元素 - - On occasion we might need to access a component's corresponding DOM element. + + On occasion we might need to access a component's corresponding DOM element. Although we strive to avoid it, many visual effects and 3rd party tools (such as jQuery) require DOM access. 偶尔,我们可能需要访问一个组件对应的DOM元素。我们尽量避免这样做,但还是有很多视觉效果和第三方工具(比如jQuery)需要访问DOM。 - - To illustrate, we've written a simplified version of the `HighlightDirective` from + + To illustrate, we've written a simplified version of the `HighlightDirective` from the [Attribute Directives](../guide/attribute-directives.html) chapter. - + 为了说明这一点,我们在[属性型指令](../guide/attribute-directives.html)`HighlightDirective`的基础上,编写了一个简化版本。 - + +makeExample('cb-dependency-injection/ts/app/highlight.directive.ts','','app/highlight.directive.ts') :marked The directive sets the background to a highlight color when the user mouses over the DOM element to which it is applied. 当用户把鼠标移到DOM元素上时,指令将该元素的背景设置为一个高亮颜色。 - - Angular set the constructor's `el` parameter to the injected `ElementRef` which is + + Angular set 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把构造函数参数`el`设置为注入的`ElementRef`,该`ElementRef`代表了宿主的DOM元素, 它的`nativeElement`属性把该DOM元素暴露给了指令。 - - The sample code applies the directive's `myHighlight` attribute to two `
` tags, + + 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. - + 下面的代码把指令的`myHighlight`属性(Attribute)填加到两个`
`标签里,一个没有赋值,一个赋值了颜色。 - + +makeExample('cb-dependency-injection/ts/app/app.component.html','highlight','app/app.component.html (highlight)')(format='.') :marked The following image shows the effect of mousing over the `` tag. - + 下图显示了鼠标移到``标签的效果: - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/highlight.png" alt="Highlighted bios") :marked @@ -614,43 +614,43 @@ figure.image-display ## Define dependencies with providers ## 使用供应商来定义依赖 - + In this section we learn to write providers that deliver dependent services. 在这个部分,我们学习如何编写供应商来提供被依赖的服务。 - + ### Background ### 背景知识 - - We get a service from a dependency injector by giving it a ***token***. + + We get a service from a dependency injector by giving it a ***token***. 我们通过给依赖注入器提供***令牌***来获取服务。 - + We usually let Angular handle this transaction for us by specifying a constructor parameter and its type. The parameter type serves as the injector lookup *token*. Angular passes this token to the injector and assigns the result to the parameter. Here's a typical example: - + 我们通常在构造函数里面,为参数指定类型,让Angular来处理依赖注入。该参数类型就是依赖注入器所需的*令牌*。 Angular把该令牌传给注入器,然后把得到的结果赋给参数。下面是一个典型的例子: -+makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','ctor','app/hero-bios.component.ts (组件构造器注入)')(format='.') ++makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','ctor','app/hero-bios.component.ts (组件构造器注入)')(format='.') :marked Angular asks the injector for the service associated with the `LoggerService` and and assigns the returned value to the `logger` parameter. Angular向注入器请求与`LoggerService`对应的服务,并将返回值赋给`logger`参数。 - + 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*. - + 注入器从哪儿得到的依赖? 它可能在自己内部容器里已经有该依赖了。 如果它没有,也能在***供应商***的帮助下新建一个。 *供应商*就是一个用于交付服务的配方,它被关联到一个令牌。 - + .l-sub-section :marked If the injector doesn't have a provider for the requested *token*, it delegates the request @@ -659,33 +659,33 @@ figure.image-display 如果注入器无法根据令牌在自己内部找到对应的供应商,它便将请求移交给它的父级注入器,这个过程不断重复,直到没有更多注入器为止。 如果没找到,注入器就抛出一个错误...除非这个请求是[可选的](#optional)。 - + Let's return our attention to providers themselves. - + 让我们把注意力转回到供应商。 :marked A new injector has no providers. - + 新建的注入器中没有供应商。 - + Angular initializes the injectors it creates with some providers it cares about. We have to register our _own_ application providers manually, usually in the `providers` array of the `Component` or `Directive` metadata: - + Angular会使用一些自带的供应商来初始化这些注入器。我们必须自行注册属于_自己_的供应商,通常用`组件`或者`指令`元数据中的`providers`数组进行注册。 - -+makeExample('cb-dependency-injection/ts/app/app.component.ts','providers','app/app.component.ts (供应商)') + ++makeExample('cb-dependency-injection/ts/app/app.component.ts','providers','app/app.component.ts (供应商)') :marked ### Defining providers ### 定义供应商 - + The simple class provider is the most typical by far. We mention the class in the `providers` array and we're done. - + 简单的类供应商是最典型的例子。我们只要在`providers`数值里面提到该类就可以了。 - -+makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','class-provider','app/hero-bios.component.ts (类供应商)')(format='.') + ++makeExample('cb-dependency-injection/ts/app/hero-bios.component.ts','class-provider','app/hero-bios.component.ts (类供应商)')(format='.') :marked 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. @@ -693,40 +693,35 @@ figure.image-display 注册类供应商之所以这么简单,是因为最常见的可注入服务就是一个类的实例。 但是,并不是所有的依赖都只要创建一个类的新实例就可以交付了。我们还需要其它的交付方式,这意味着我们也需要其它方式来指定供应商。 - - The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them. + + The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them. `HeroOfTheMonthComponent`例子示范了一些备选方案,展示了我们为什么需要它们。 - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px") :marked It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about. - + 它看起来很简单:一些属性和一个日志输出。但代码的背后有很多可讨论的地方。 - -+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','hero-of-the-month','hero-of-the-month.component.ts') + ++makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','hero-of-the-month','hero-of-the-month.component.ts') .l-main-section a(id='provide') :marked - #### The *provide* function + #### The *provide* object literal + + #### *provide*对象 - #### *provide*函数 - - The imported Angular `provide` function creates an instance of - the Angular [Provider](../api/core/Provider-class.html) class. - - 被导入的Angular `provide`函数新建一个Angular [Provider](../api/core/Provider-class.html)类的实例。 - - The `provide` function takes a *token* and a *definition object*. + The `provide` object literal takes a *token* and a *definition object*. The *token* is usually a class but [it doesn't have to be](#tokens). - 该`provide`函数需要一个*令牌*和一个*定义对象*。该*令牌*通常是一个类,但[并非一定是](#tokens) - - The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider + 该`provide`对象需要一个*令牌*和一个*定义对象*。该*令牌*通常是一个类,但[并非一定是](#tokens) + + The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider should create or return the provided value. - + 该*定义*对象有一个主属性(即`userValue`),用来标识该供应商会如何新建和返回依赖。 .l-main-section @@ -735,48 +730,48 @@ a(id='usevalue') #### useValue - the *value provider* #### useValue - *值-供应商* - + Set the `useValue` property to a ***fixed value*** that the provider can return as the dependency object. 把一个***固定的值**,也就是该供应商可以将其作为依赖对象返回的值,赋给`userValue`属性。 - + Use this technique to provide *runtime configuration constants* such as web-site base addresses and feature flags. We often use a *value provider* in a unit test to replace a production service with a fake or mock. 使用该技巧来进行*运行期常量设置*,比如网站的基础地址和功能标志等。 我们通常在单元测试中使用*值-供应商*,用一个假的或模仿的(服务)来取代一个生产环境的服务。 - + The `HeroOfTheMonthComponent` example has two *value providers*. The first provides an instance of the `Hero` class; the second specifies a literal string resource: - + `HeroOfTheMonthComponent`例子有两个*值-供应商*。 第一个提供了一个`Hero`类的实例;第二个指定了一个字符串资源: - -+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-value')(format='.') + ++makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-value')(format='.') :marked 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. `Hero`供应商的令牌是一个类,这很合理,因为它提供的结果是一个`Hero`实例,并且被注入该英雄的消费者也需要知道它类型信息。 - + The `TITLE` provider token is *not a class*. It's a special kind of provider lookup key called an [OpaqueToken](#opaquetoken). We often use an `OpaqueToken` when the dependency is a simple value like a string, a number, or a function. `TITLE` 供应商的令牌*不是一个类*。它是一个特别类型的供应商查询键,名叫[OpaqueToken](#opaquetoken). - - The value of a *value provider* must be defined *now*. We can't create the value later. + + The value of a *value provider* must be defined *now*. We 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: - + 一个*值-供应商*的值必须要*立即*定义。我们不能事后再定义它的值。很显然,标题字符串是立刻可用的。 该例中的`someHero`变量是以前在下面这个文件中定义的: - + +makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','some-hero') :marked The other providers create their values *lazily* when they're needed for injection. - + 其它供应商只在需要注入它们的时候才创建并*延迟加载*它们的值。 .l-main-section @@ -785,22 +780,22 @@ a(id='useclass') #### useClass - the *class provider* #### useClass - *类-供应商* - + The `useClass` provider creates and returns new instance of the specified class. `userClass`供应商创建并返回一个指定类的新实例。 - + 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. 使用该技术来为公共或默认类***提供候选实现***。该候选项能实现一个不同的策略,比如拓展默认类或者在测试的时候假冒真实类。 - + We see two examples in the `HeroOfTheMonthComponent`: - + 请看下面`HeroOfTheMonthComponent`里的两个例子: - -+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-class')(format='.') + ++makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-class')(format='.') :marked 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 injection token. @@ -808,25 +803,25 @@ a(id='useclass') 第一个供应商是*展开了语法糖的*,是一个典型情况的展开。一般来说,被新建的类(`HeroService`)同时也是该供应商的注入令牌。 我们这里用完整形态来编写它,来反衬我们更喜欢的缩写形式。 - + 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. - + 第二个供应商使用`DateLoggerService`来满足`LoggerService`。该`LoggerService`在`AppComponent`级别已经被注册。当_这个组件_要求`LoggerService`的时候,它得到的却是`DateLoggerService`服务。 - + .l-sub-section :marked This component and its tree of child components receive the `DateLoggerService` instance. Components outside the tree continue to receive the original `LoggerService` instance. - + 这个组件及其子组件会得到`DateLoggerService`实例。这个组件树之外的组件得到的仍是`LoggerService`实例。 :marked The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message: - + `DateLoggerService`从`LoggerService`继承;它把当前的日期/时间附加到每条信息上。 - -+makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','date-logger-service','app/date-logger.service.ts')(format='.') + ++makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','date-logger-service','app/date-logger.service.ts')(format='.') .l-main-section a(id='useexisting') @@ -834,40 +829,40 @@ a(id='useexisting') #### useExisting - the *alias provider* #### useExisting - *别名-供应商* - - The `useExisting` provider maps one token to another. + + The `useExisting` provider maps one token to another. In effect, the first token is an ***alias*** for the service associated with second token, creating ***two ways to access the same service object***. - + 使用`useExisting`,供应商可以把一个令牌映射到另一个令牌上。实际上,第一个令牌是第二个令牌所对应的服务的一个***别名***,创造了***访问同一个服务对象的两种方法***。 - + +makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-existing') :marked Narrowing an API through an aliasing interface is _one_ important use case for this technique. We're aliasing for that very purpose here. Imagine that the `LoggerService` had a large API (it's actually only three methods and a property). We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](#class-interface): - + 通过使用别名接口来把一个API变窄,是_一个_很重要的该技巧的使用例子。我们在这里就是为了这个目的使用的别名。 想象一下如果`LoggerService`有个很大的API接口(虽然它其实只有三个方法,一个属性),我们通过使用`MinimalLogger`[*类-接口*](#class-interface)别名,就能成功的把这个API接口缩小到只暴露两个成员: +makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','minimal-logger','app/date-logger.service.ts (MinimalLogger)')(format='.') :marked The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript: - + 构造函数的`logger`参数是一个`MinimalLogger`类型,所有在TypeScript里面,它只有两个成员可见: - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger受限API") :marked Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService` which happens to be the `DateLoggerService` thanks to the override provider registered previously via `useClass`. The following image, which displays the logging date, confirms the point: - + 实际上,Angular确实想把`logger`参数设置为注入器里`LoggerService`的完整版本。只是在之前的供应商注册里使用了`useClass`, 所以该完整版本被`DateLoggerService`取代了。 在下面的图片中,显示了日志日期,可以确认这一点: - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px") @@ -877,175 +872,158 @@ a(id='usefactory') #### useFactory - the *factory provider* #### useFactory - *工厂-供应商* - + The `useFactory` provider creates a dependency object by calling a factory function as seen in this example. - + `useFactory` 供应商通过调用工厂函数来新建一个依赖对象,如下例所示。 - + +makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-factory') :marked 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 to the "Hero of the Month" contest. - + 该*依赖对象*不一定是一个类实例。它可以是任何东西。在这个例子里,*依赖对象*是一个字符串,代表了**本月英雄**比赛的亚军的名字。 The local state is the number `2`, the number of runners-up this component should show. We execute `runnersUpFactory` immediately with `2`. 本地状态是数字`2`,该组件应该显示的亚军的个数。我们立刻用`2`来执行`runnersUpFactory`。 - + The `runnersUpFactory` itself isn't the provider factory function. The true provider factory function is the function that `runnersUpFactory` returns. `runnersUpFactory`自身不是供应商工厂函数。真正的供应商工厂函数是`runnersUpFactory`返回的函数。 - -+makeExample('cb-dependency-injection/ts/app/runners-up.ts','factory-synopsis','runners-up.ts (excerpt)')(format='.') + ++makeExample('cb-dependency-injection/ts/app/runners-up.ts','factory-synopsis','runners-up.ts (excerpt)')(format='.') :marked That returned function takes a winning `Hero` and a `HeroService` as arguments. 这个返回的函数需要一个`Hero`和一个`HeroService`参数。 - - Angular supplies these arguments from injected values identified by + + 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. Angular通过使用`deps`数组中的两个*令牌*,来识别注入的值,用来提供这些参数。这两个`deps`值是供注入器使用的*令牌*,用来提供工厂函数的依赖。 - - After some undisclosed work, the function returns the string of names + + After some undisclosed work, the function returns the string of names and Angular injects it into the `runnersUp` parameter of the `HeroOfTheMonthComponent`. 一些内部工作后,这个函数返回名字字符串,Angular将其注入到`HeroOfTheMonthComponent`组件的`runnersUp`参数里。 - + .l-sub-section :marked 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 [live example](/resources/live-examples/cb-dependency-injection/ts/plnkr.html) for the full source code. - + 该函数从`HeroService`获取英雄参赛者,从中取`2`个作为亚军,并把他们的名字拼接起来。请到[在线例子](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)查看全部原代码。 - -.l-main-section -:marked - ## Define providers with object literals - - ## 使用对象文本定义供应商 - -:marked - In the previous section we learned to use the `provide` method to customize how dependencies are created. - - Instead of calling the `provide` function, we can configure providers with _object literals_. - It's a slightly more concise syntax and we don't have to import the `provide` function. - - Here's a set of providers that we saw earlier, re-written with object literals. - -+makeExample('cb-dependency-injection/ts/app/hero-of-the-month-literals.component.ts','providers-using-object-literals')(format='.') - a(id="tokens") .l-main-section :marked ## Provider token alternatives: the *class-interface* and *OpaqueToken* - + ## 可选供应商令牌:*类-接口*和*OpaqueToken* Angular dependency injection is easiest when the provider *token* is a class that is also the type of the returned dependency object (what we usually call the *service*). Angular依赖注入当*令牌*是类的时候是最简单的,该类同时也是返回的依赖对象的类型(我们通常直接称之为*服务*)。 - + But the 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 our next section. 但令牌不一定都是类,就算它是一个类,它也不一定都返回类型相同的对象。这是我们下一节的主题。 - + ### class-interface ### 类-接口 In the previous *Hero of the Month* example, we used the `MinimalLogger` class as the token for a provider of a `LoggerService`. - + 在前面的*每月英雄*的例子中,我们用了`MinimalLogger`类作为`LoggerService` 供应商的令牌。 - + +makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','use-existing') :marked The `MinimalLogger` is an abstract class. - + 该`MinimalLogger`是一个抽象类。 - -+makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','minimal-logger')(format='.') + ++makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','minimal-logger')(format='.') :marked We usually inherit from an abstract class. But `LoggerService` doesn't inherit from `MinimalLogger`. *No class* inherits from it. Instead, we use it like an interface. 我们通常从一个抽象类继承。但`LoggerService`并不继承`MinimalLogger`。*没有类*会继承它。我们只把它当接口来使用。 - + Look again at the declaration for `DateLoggerService` - + 请再看下`DateLoggerService`的声明 - -+makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','date-logger-service-signature')(format='.') + ++makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','date-logger-service-signature')(format='.') :marked `DateLoggerService` inherits (extends) from `LoggerService`, not `MinimalLogger`. The `DateLoggerService` *implements* `MinimalLogger` as if `MinimalLogger` were an *interface*. `DateLoggerService`继承(扩展)了`LoggerService`,而不是`MinimalLogger`。该`DateLoggerService`*实现了*`MinimalLogger`,就像`MinimalLogger`是一个接口一样。 - + We call a class used in this way a ***class-interface***. The key benefit of a *class-interface* is that we can get the strong-typing of an interface and we can ***use it as a provider token*** in the same manner as a normal class. 我们称这种用法的类叫做*类-接口*。它关键的好处是:给我们提供了接口的强类型,同时,我们能像正常类一样***把它当做供应商令牌使用***。 - + 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. The `MinimalLogger` defines just two of the `LoggerClass` members. - + ***类-接口***应该*只*定义它的消费者允许调用的成员。窄的接口有助于我们解耦该类的具体实现和它的消费者。 该`MinimalLogger`只定义了两个`LoggerClass`的成员。 .l-sub-section :marked #### Why *MinimalLogger* is a class and not an interface - + #### 为什么*MinimalLogger*是一个类而不是一个接口 - - We can't use an interface as a provider token because + + We 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. 我们不能把接口当做供应商的令牌,因为接口不是有效的JavaScript对象。 它们只存在在TypeScript的设计空间里。它们会在被编译为JavaScript之后消失。 - - A provider token must be a real JavaScript object of some kind: + + A provider token must be a real JavaScript object of some kind: a function, an object, a string ... a class. 一个供应商令牌必须是一个真实的JavaScript对象,比如:一个函数,一个对象,一个字符串 ...一个类。 - + Using a class as an interface gives us the characteristics of an interface in a JavaScript object. 把类当做接口使用,可以为我们在一个JavaScript对象上提供类似于接口的特性。 - - The minimize memory cost, the class should have *no implementation*. + + The minimize memory cost, the class should have *no implementation*. The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript: - + 为了节省内存占用,该类应该***没有具体的实现***。`MinimalLogger`会被转译成下面这段没有优化过的,尚未最小化的JavaScript: - + +makeExample('cb-dependency-injection/ts/app/date-logger.service.ts','minimal-logger-transpiled')(format='.') :marked It never grows larger no matter how many members we add *as long as they are typed but not implemented*. - + ***只要我们不实现它***,不管我们添加多少成员,它永远不会增长大小。 a(id='opaque-token') @@ -1053,53 +1031,53 @@ a(id='opaque-token') ### OpaqueToken ### OpaqueToken - - Dependency objects can be simple values like dates, numbers and strings or + + Dependency objects can be simple values like dates, numbers and strings or shapeless objects like arrays and functions. 依赖对象可以是一个简单的值,比如日期,数字和字符串,或者一个无形的对象,比如数组和函数。 - + Such objects don't have application interfaces and therefore aren't well represented by a class. 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. 这样的对象没有应用程序接口,所以不能用一个类来表示。更适合表示它们的是:唯一的和符号性的令牌,一个JavaScript对象,拥有一个友好的名字,但不会与其它的同名令牌发生冲突。 - + The `OpaqueToken` has these characteristics. We encountered them twice in the *Hero of the Month* example, in the *title* value provider and in the *runnersUp* factory provider. - + `OpaqueToken`具有这些特征。我们在*Hero of the Month*例子中遇见它们两次,一个是*title*的值,一个是*runnersUp* 工厂供应商。 - -+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','provide-opaque-token')(format='.') + ++makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','provide-opaque-token')(format='.') :marked We created the `TITLE` token like this: - + 我们这样创建`TITLE`令牌: - -+makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','opaque-token')(format='.') + ++makeExample('cb-dependency-injection/ts/app/hero-of-the-month.component.ts','opaque-token')(format='.') a(id="di-inheritance") .l-main-section :marked ## Inject into a derived class - + ## 注入到一个派生类 - + We must take care when writing a component that inherits from another component. If the base component has injected dependencies, we must re-provide and re-inject them in the derived class and then pass them down to the base class through the constructor. 当编写一个继承自另一个组件的组件时,我们要格外小心。如果基础组件有依赖注入,我们必须要在派生类中重新提供和重新注入它们,并将它们通过构造函数传给基类。 - - In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent` + + In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent` to display a *sorted* list of heroes. 在这个生造的例子里,`SortedHeroesComponent`继承自`HeroesBaseComponent`,显示一个*被排序*的英雄列表。 - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/sorted-heroes.png" alt="Sorted Heroes") :marked @@ -1108,8 +1086,8 @@ figure.image-display and displays them in the order they arrive from the database. `HeroesBaseComponent`能自己独立运行。它在自己的实例里要求`HeroService`,用来得到英雄,并将它们按照数据库返回的顺序显示出来。 - -+makeExample('cb-dependency-injection/ts/app/sorted-heroes.component.ts','heroes-base','app/sorted-heroes.component.ts (HeroesBaseComponent)') + ++makeExample('cb-dependency-injection/ts/app/sorted-heroes.component.ts','heroes-base','app/sorted-heroes.component.ts (HeroesBaseComponent)') .l-sub-section :marked We strongly prefer simple constructors. They should do little more than initialize variables. @@ -1118,11 +1096,11 @@ figure.image-display 我们强烈推荐简单的构造函数。它们应该***只***用来初始化变量。这个规则会帮助我们在测试环境中放心的构造组件,以免在构造它们时,无意做了一些非常戏剧化的动作(比如连接服务)。 这就是为什么我们要在`ngOnInit`里面调用`HeroService`,而不是在构造函数中。 - + We explain the mysterious `afterGetHeroes` below. - + 我们在下面解释这个神秘的`afterGetHeroes`。 - + :marked Users want to see the heroes in alphabetical order. Rather than modify the original component, we sub-class it and create a @@ -1132,13 +1110,13 @@ figure.image-display 用户希望看到英雄按字母顺序排序。与其修改原始的组件,不如派生它,新建`SortedHeroesComponent`,以便展示英雄之前进行排序。 `SortedHeroesComponent`让基类来获取英雄。(我们说过这是生造的,仅用来解释这种机制)。 - + Unfortunately, Angular cannot inject the `HeroService` directly into the base class. We must provide the `HeroService` again for *this* component, then pass it down to the base class inside the constructor. - + 可惜,Angular不能直接在基类里直接注入`HeroService`。我们必须在*这个*组件里再次提供`HeroService`,然后通过构造函数传给基类。 - + +makeExample('cb-dependency-injection/ts/app/sorted-heroes.component.ts','sorted-heroes','app/sorted-heroes.component.ts (SortedHeroesComponent)') :marked Now take note of the `afterGetHeroes` method. @@ -1146,16 +1124,16 @@ figure.image-display But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit` so we'd be sorting the heroes array *before they arrived*. That produces a nasty error. - 现在,请注意`_afterGetHeroes`方法。 + 现在,请注意`_afterGetHeroes`方法。 我们第一反应是在`SortedHeroesComponent`组件里面建一个`ngOnInit`方法来做排序。但是Angular会先调用*派生*类的`ngOnInit`,后调用基类的`ngOnInit`, 所以我们可能在*英雄到达之前*就开始排序。这就产生了一个讨厌的错误。 - + Overriding the base class's `afterGetHeroes` method solves the problem 覆盖基类的`afterGetHeroes`方法可以解决这个问题。 - - These complications argue for *avoiding component inheritance*. - + + These complications argue for *avoiding component inheritance*. + 分析上面的这些复杂性是为了强调*避免使用组件继承*这一点。 a(id="find-parent") @@ -1164,113 +1142,113 @@ a(id="find-parent") ## Find a parent component by injection ## 通过注入来找到一个父组件 - + Application components often need to share information. We prefer the more loosely coupled techniques such as data binding and service sharing. 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. 在Angular里,获取一个组件的引用比较复杂。虽然Angular应用程序是一个组件树,但它没有公开的API来在该树中巡查和穿梭。 - + There is an API for acquiring a child reference (checkout `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`). 有一个API可以获取子级的引用(请看`Query`, `QueryList`, `ViewChildren`,和`ContentChildren`)。 - + There is no public API for acquiring a parent reference. But because every component instance is added to an injector's container, we can use Angular dependency injection to reach a parent component. 但没有公开的API来获取父组件的引用。但是因为每个组件的实例都被加到了依赖注入器的容器中,我们可以使用Angular依赖注入来找到父组件。 - + This section describes some techniques for doing that. 本章节描述了这项技术。 - + ### Find a parent component of known type ### 找到已知类型的父组件 - + We use standard class injection to acquire a parent component whose type we know. 我们使用标准的类注入来获取已知类型的父组件。 - + In the following example, the parent `AlexComponent` has several children including a `CathyComponent`: - + 在下面的例子中,父组件`AlexComponent`有几个子组件,包括`CathyComponent`: - + a(id='alex') +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-1','parent-finder.component.ts (AlexComponent v.1)')(format='.') :marked *Cathy* reports whether or not she has access to *Alex* after injecting an `AlexComponent` into her constructor: - + 在注入*AlexComponent`进来后,*Cathy*报告它是否对*Alex*有访问权: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','cathy','parent-finder.component.ts (CathyComponent)')(format='.') :marked We added the [@Optional](#optional) qualifier for safety but the [live example](/resources/live-examples/cb-dependency-injection/ts/plnkr.html) confirms that the `alex` parameter is set. - + 安全起见,我们添加了[@Optional](#optional)装饰器,但是[在线例子](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)显示`alex`参数确实被设置了。 - + ### Cannot find a parent by its base class ### 无法通过它的基类找到一个父级 - + What if we do *not* 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 sound (cough) business reasons, this news component makes frequent calls directly into its parent instrument as changing market data stream by. 一个可复用的组件可能是多个组件的子级。想象一个用来渲染金融工具头条新闻的组件。为了合理(咳咳)的商业理由,该新闻组件在实时变化的市场数据流过时,要频繁的直接调用其父级工具。 - + The app probably defines more than a dozen financial instrument components. If we're lucky, they all implement the same base class whose API our `NewsComponent` understands. 该应用程序可能有多于一打的金融工具组件。如果幸运,它们可能会从同一个基类派生,其API是我们的`NewsComponent`组件所能理解的。 - + .l-sub-section :marked 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 we could look for. - + 更好的方式是通过接口来寻找实现了它的组件。但这是不可能的,因为TypeScript的接口在编译成JavaScript以后就消失了,JavaScript不支持接口。我们没有东西可查。 - + :marked We're not claiming this is good design. We are asking *can a component inject its parent via the parent's base class*? 这并不是好的设计。我们的问题是*一个组件是否能通过它父组件的基类来注入它的父组件呢*? - - The sample's `CraigComponent` explores this question. [Looking back](#alex) + + The sample's `CraigComponent` explores this question. [Looking back](#alex) we see that the `Alex` component *extends* (*inherits*) from a class named `Base`. - + `CraigComponent`例子探究了这个问题。[往回看Alex]{#alex},我们看到`Alex`组件*扩展*(*派生*)自一个叫`Base`的类。 - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (Alex class signature)')(format='.') :marked The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded. - + `CraigComponent`试图把`Base`注入到到它的`alex`构造函数参数,来报告是否成功。 - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','craig','parent-finder.component.ts (CraigComponent)')(format='.') :marked Unfortunately, this does not work. @@ -1280,50 +1258,50 @@ a(id='alex') 可惜这样不行。[在线例子](/resources/live-examples/cb-dependency-injection/ts/plnkr.html)显示`alex`参数是null。 *我们不能通过基类注入父组件*。 - + ### Find a parent by its class-interface ### 通过类-接口找到父组件 - + We can find a parent component with a [class-interface](#class-interface). - + 我们可以通过[类-接口](#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 we could inject *Alex* into *Carol* [earlier](#known-parent). - + 请记住Angular总是从它自己的注入器添加一个组件实例;这就是为什么在[之前](#known-parent)我们可以*Alex*注入到*Carol*。 - We write an [*alias provider*](#useexisting) — a `provide` function with a `useExisting` definition — + We write an [*alias provider*](#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`: - + 我们编写一个[*别名供应商*](#useexisting) —一个拥有`useExisting`定义的`provide`函数 — 它新建一个*备选的*方式来注入同一个组件实例,并把这个供应商添加到`AlexComponent`的`@Component`元数据里的`providers`数组。 - + a(id="alex-providers") +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.') :marked [Parent](#parent-token) is the provider's *class-interface* token. The [*forwardRef*](#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself. - + [Parent](#parent-token)是该供应商的*类-接口*令牌。`AlexComponent`引用了自身,造成循环引用,我们使用[*forwardRef*](#forwardRef)打破了该循环。 - + *Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before: - + *Carol*,*Alex*的第三个子组件,把父级注入到了自己的`parent`参数,和我们之前做的一样: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','carol-class','parent-finder.component.ts (CarolComponent class)')(format='.') :marked Here's *Alex* and family in action: - + 下面是*Alex*和其家庭的运行结果: - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/alex.png" alt="Alex in action") @@ -1331,23 +1309,23 @@ a(id="parent-tree") :marked ### Find the parent in a tree of parents ### 通过父级树找到父组件 - + Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*. Both *Alice* and *Barry* implement the `Parent` *class-interface*. 想象组件树中的一个分支为:*Alice* -> *Barry* -> *Carol*。 *Alice*和*Barry*都实现了这个`Parent`*类-接口*。 - + *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*. *Barry*是个问题。他需要访问它的父组件*Alice*,但同时它也是*Carol*的父组件。这个意味着它必须同时*注入*`Parent`*类-接口*来获取*Alice*,和*提供*一个`Parent`来满足*Carol*。 - + Here's *Barry*: - + 下面是*Barry*的代码: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','barry','parent-finder.component.ts (BarryComponent)')(format='.') :marked *Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers). @@ -1355,11 +1333,11 @@ a(id="parent-tree") *Barry*的`providers`数组看起来很像[*Alex*的那个](#alex-providers). 如果我们准备一直像这样编写[*别名供应商*](#useexisting)的话,我们应该建立一个[帮助函数](#provideparent)。 - + For now, focus on *Barry*'s constructor: - + 眼下,请注意*Barry*的构造函数: - + +makeTabs( 'cb-dependency-injection/ts/app/parent-finder.component.ts, cb-dependency-injection/ts/app/parent-finder.component.ts', 'barry-ctor, carol-ctor', @@ -1369,26 +1347,26 @@ a(id="parent-tree") It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator. 除添加了一个额外的`@SkipSelf`外,它和*Carol*的构造函数一样。 - + `@SkipSelf` is essential for two reasons: - + 添加`@SkipSelf`主要是出于两个原因: - + 1. It tell the injector to start its search for a `Parent` dependency in a component *above* itself, which *is* what parent means. 1. 它告诉注入器从一个在自己*上一级*的组件开始搜索一个`Parent`依赖。 - + 2. Angular throws a cyclic dependency error if we omit the `@SkipSelf` decorator. 2. 如果我们没写`@SkipSelf`装饰器的话,Angular就会抛出一个循环依赖错误。 - + `Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)` - + `不能创建循环依赖实例!(BethComponent -> Parent -> BethComponent)` Here's *Alice*, *Barry* and family in action: - + 这里是*Alice*,*Barry*和该家庭的代码: figure.image-display @@ -1397,38 +1375,38 @@ figure.image-display a(id="parent-token") :marked ### The *Parent* class-interface - + ### *Parent*类-接口 - + We [learned earlier](#class-interface) that a *class-interface* is an abstract class used as an interface rather than as a base class. - + 我们[以前学过](#class-interface):*类-接口*是一个抽象类,被当成一个接口使用,而非基类。 Our example defines a `Parent` *class-interface* . - + 我们的例子定义了一个`Parent`*类-接口*。 - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','parent','parent-finder.component.ts (Parent class-interface)')(format='.') :marked 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 narrowing interface helps decouple the child component class from its parent components. - + 该`Parent`*类-接口*定义了`Name`属性,它有类型声明,但是*没有实现*,该`name`是该父级的所有子组件们唯一能调用的属性。 这种“窄接口”有助于解耦子组件类和它的父组件。 - + A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does: - + 一个能用做父级的组件*应该*实现*类-接口*,和下面的`AliceComponent`的做法一样: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alice-class-signature','parent-finder.component.ts (AliceComponent class signature)')(format='.') :marked 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`: - + 这样做可以提升代码的清晰度。但在技术上,这并不是必须的。虽然`AlexComponent`有一个`name`属性(来自`Base`类的要求),但它的类签名并不需要提及`Parent`。 - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (AlexComponent class signature)')(format='.') .l-sub-section :marked @@ -1436,47 +1414,47 @@ a(id="parent-token") It doesn't in this example *only* to demonstrate that the code will compile and run without the interface 为了正确的代码风格,该`AlexComponent`*应该*实现`Parent`。在这个例子里它没有这样,只是为了演示在没有该接口的情况下,该代码仍会被正确编译并运行。 - + a(id="provideparent") :marked ### A *provideParent* helper function ### *provideParent*助手函数 - - Writing variations of the same parent *alias provider* gets old quickly, - especially this awful mouthful with a [*forwardRef*](#forwardref): - + + Writing variations of the same parent *alias provider* gets old quickly, + especially this awful mouthful with a [*forwardRef*](#forwardref): + 编写父组件相同的各种*别名供应商*很快就会变得啰嗦,在用[*forwardRef](#forwardRef)的时候尤其绕口: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-providers')(format='.') :marked We can extract that logic into a helper function like this: - + 我们可以像这样把该逻辑抽取到一个助手函数里: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','provide-the-parent')(format='.') :marked Now we can add a simpler, more meaningful parent provider to our components: - + 现在就可以为我们的组件添加一个更简单、直观的父级供应商了: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alice-providers')(format='.') :marked We can do better. The current version of the helper function can only alias the `Parent` *class-interface*. Our application might have a variety of parent types, each with its own *class-interface* token. 我们可以做得更好。当前版本的助手函数只能为`Parent`*类-接口*提供别名。我们的应用程序可能有很多类型的父组件,每个父组件有自己的*类-接口*令牌。 - + Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*. - + 下面是一个修改版本,默认接受一个`Parent`,但同时接受一个可选的第二参数,可以用来指定一个不同的父级*类-接口*。 - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','provide-parent')(format='.') :marked And here's how we could use it with a different parent type: - + 下面的代码演示了我们要如何使它添加一个不同类型的父级: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','beth-providers')(format='.') :marked @@ -1486,12 +1464,12 @@ a(id="forwardref") ## Break circularities with a forward class reference (*forwardRef*) ## 使用一个前向引用(*forwardRef*)来打破循环 - + The order of class declaration matters in TypeScript. We can't refer directly to a class until it's been defined. 在TypeScript里面,类声明的顺序是很重要的。如果一个类尚未定义,我们就不能引用它。 - + This isn't usually a problem, especially if we adhere to the recommended *one class per file* rule. But sometimes circular references are unavoidable. We're in a bind when class 'A refers to class 'B' and 'B' refers to 'A'. @@ -1499,37 +1477,37 @@ a(id="forwardref") 这通常不是一个问题,特别是当我们遵循*一个文件一个类*规则的时候。 但是有时候循环引用可能不能避免。当一个类*A引用类B*,同时'B'引用'A'的时候,我们就陷入困境了:它们中间的某一个必须要先定义。 - + The Angular `forwardRef` function creates an *indirect* reference that Angular can resolve later. Angular的`forwardRef`函数建立一个*间接地*引用,Angular可以随后解析。 - + The *Parent Finder* sample is full of circular class references that are impossible to break. 这个*父组件查找器*例子里,到处都是我们无法打破的类循环引用。 - + In the [*Alex/Cathy* example](#known-parent) above: 在上面的[*Alex/Cathy*例子](#known-parent)中: - + * the `AlexComponent` lists the `CathyComponent` in its component metadata `directives` array so it can display *Cathy* in its template. * `AlexComponent`在它的组件元数据`Directives`数值里面列出`CathyComponent`,这样它可以在自己的模板中显示*Cathy*。 - + * the `CathyComponent` constructor injects the parent `AlexComponent` which means that the `alex` parameter of its constructor has the `AlexComponent` type. * `CathyComponent`的构造函数注入父级`AlexComponent`,这样的话,构造函数参数`alex`是`AlexComponent`类型。 - - *Alex* refers to *Cathy* and *Cathy* refers to *Alex*. We're stuck. We must define one of them first. + + *Alex* refers to *Cathy* and *Cathy* refers to *Alex*. We're stuck. We must define one of them first. *Alex* 引用了*Cathy*,同时,*Cathy*引用了*Alex*。我们被卡住了。我们必须要先定义它们中的一个。 - + We defined *Alex* first and built its `C_DIRECTIVES` array with a forward reference to *Cathy*: - + 我们先定义了*Alex*,然后使用*Cathy*的前向引用来创建`C_DIRECTIVES`数列: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','C_DIRECTIVES','parent-finder.component.ts (C_DIRECTIVES)')(format='.') :marked .l-sub-section @@ -1538,13 +1516,13 @@ a(id="forwardref") *Alex* would have to import *Cathy* and *Cathy* would have to import *Alex*. 即使在单独的文件里面定义*Alex*和*Cathy*也没用,因为*Alex*必须导入*Cathy*,而*Cathy*又必须导入*Alex*。 - - We *had* to define *Alex* first because, + + We *had* to define *Alex* first because, while we can add `forwardRef(CathyComponent)` to *Alex*'s `directives` array, we can't write `public alex: forwardRef(AlexComponent))` in *Cathy*'s constructor. - + 我们*只能*先定义*Alex*,因为我们可以添加`forwardRef(CathyComponent)`到*Alex*的`Directives`数组里面,但是我们不能在*Cathy*的构造函数里面使用`public alex: forwardRef(AlexComponent))`。 - + :marked We face a similar dilemma when a class makes *a reference to itself* as does the `AlexComponent` in its `providers` array. @@ -1553,10 +1531,10 @@ a(id="forwardref") 当一个类*需要引用自身*的时候,我们面临同样的窘境,就像在`AlexComponent`的`provdiers`数组中遇到的困境一样。 该`providers`数组是一个`@Component`装饰器函数的一个属性,它必须在类定义*之前*出现。 - + Again we break the circularity with `forwardRef`: - + 我们再次使用`forwardRef`来打破该循环: - + +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.') :marked diff --git a/public/docs/ts/latest/guide/component-styles.jade b/public/docs/ts/latest/guide/component-styles.jade index ace06c1869..7e5a8bf1c2 100644 --- a/public/docs/ts/latest/guide/component-styles.jade +++ b/public/docs/ts/latest/guide/component-styles.jade @@ -7,16 +7,16 @@ block includes to our Angular applications directly. Angular 2应用使用标准的CSS来设置样式。这意味着我们可以把关于CSS的那些知识和技能直接用于我们的Angular程序中,比如:样式表、选择器、规则,以及媒体查询等。 - + On top of this, Angular has the ability to bundle *component styles* with our components enabling a more modular design than regular stylesheets. 在此基础上,Angular还能把*组件样式*紧紧的“捆绑”在我们的组件上,以实现一种比标准样式表更加模块化的设计。 - + In this chapter we learn how to load and apply these *component styles*. 在本章中,我们将学到如何加载和使用这些*组件样式*。 - + # Table Of Contents # 目录 @@ -40,11 +40,11 @@ p 运行本章这些代码的#[+liveExampleLink2("在线例子")] :marked ## Using Component Styles ## 使用组件样式 - + For every Angular 2 component we write, we may define not only an HTML template, but also the CSS styles that go with that template, specifying any selectors, rules, and media queries that we need. - + 对于我们写的每个Angular 2组件来说,除了定义HTML模板之外,我们还要用于模板的CSS样式、 指定需要的选择器、规则和媒体查询。 @@ -55,14 +55,14 @@ p 运行本章这些代码的#[+liveExampleLink2("在线例子")] 它的实现方式之一,是在组件的元数据中设置`styles`属性。 `styles`属性可以接受一个包含CSS代码的字符串数组。 通常我们只给它一个字符串就行了,如同下例: - + +makeExample('component-styles/ts/app/hero-app.component.ts')(format='.') :marked Component styles differ from traditional, global styles in a couple of ways. 组件样式在很多方面都不同于传统的全局性样式。 - + Firstly, the selectors we put into a component's styles *only apply within the template of that component*. The `h1` selector in the example above only applies to the `

` tag in the template of `HeroAppComponent`. Any `

` elements elsewhere in @@ -70,32 +70,32 @@ p 运行本章这些代码的#[+liveExampleLink2("在线例子")] 首先,我们放在组件样式中的选择器,只会应用在组件自身的模板中。上面这个例子中的`h1`选择器只会对 `HeroAppComponent`模板中的`

`标签生效,而对应用中其它地方的`

`元素毫无影响。 - + This is a big improvement in modularity compared to how CSS traditionally works: 这种模块化相对于CSS的传统工作方式是一个巨大的改进: - - 1. We can use the CSS class names and selectors that make the most sense in the context of each component. - + + 1. We can use the CSS class names and selectors that make the most sense in the context of each component. + 1. 只有在每个组件的情境中使用CSS类名和选择器,才是最有意义的。 - + 1. Class names and selectors are local to the component and won't collide with classes and selectors used elsewhere in the application. - + 1. 类名和选择器是仅属于组件内部的,它不会和应用中其它地方的类名和选择器出现冲突。 - + 1. Our component's styles *cannot* be changed by changes to styles elsewhere in the application. - + 1. 我们组件的样式*不会*因为别的地方修改了样式而被意外改变。 - + 1. We can co-locate the CSS code of each component with the TypeScript and HTML code of the component, which leads to a neat and tidy project structure. 1. 我们可以让每个组件的CSS代码和它的TypeScript代码、HTML代码放在一起,这将促成清爽整洁的项目结构。 - + 1. We can change or remove component CSS code in the future without trawling through the whole application to see where else it may have been used. We just look at the component we're in. - + 1. 将来我们可以修改或移除组件的CSS代码,而不用遍历整个应用来看它有没有被别处用到,只要看看当前组件就可以了。 a(id="special-selectors") @@ -103,12 +103,12 @@ a(id="special-selectors") :marked ## Special selectors ## 特殊的选择器 - + Component styles have a few special *selectors* from the world of [shadow DOM style scoping](https://www.w3.org/TR/css-scoping-1): “组件样式”中有一些特殊的*选择器*,它们是从[局限化CSS](https://www.w3.org/TR/css-scoping-1)的世界里引入的Shadow DOM(影子DOM)选择器。 - + ### :host ### :host @@ -116,7 +116,7 @@ a(id="special-selectors") targeting elements *inside* the component's template): 使用`:host`伪类选择器,用来选择组件*宿主*元素中的元素(相对于组件模板*内部*的元素)。 - + +makeExample('component-styles/ts/app/hero-details.component.css', 'host')(format='.') :marked @@ -125,66 +125,66 @@ a(id="special-selectors") component's own template. It is in a parent component's template. 这是我们能以宿主元素为目标的*唯一*方式。除此之外,我们将没办法指定它,因为宿主不是组件自身模板的一部分,而是父组件模板的一部分。 - - Use the *function form* to apply host styles conditionally by + + Use the *function form* to apply host styles conditionally by including another selector inside parentheses after `:host`. 要把宿主样式作为条件,就要像*函数*一样把其它选择器放在`:host`后面的括号中。 - + In the next example we target the host element again, but only when it also has the `active` CSS class. 在下一个例子中,我们又一次把宿主元素作为目标,但是只有当它同时带有`active` CSS类的时候才会生效。 - + +makeExample('component-styles/ts/app/hero-details.component.css', 'hostfunction')(format=".") :marked ### :host-context ### :host-context - + Sometimes it is useful to apply styles based on some condition *outside* a component's view. For example, there may be a CSS theme class applied to the document `` element, and we want to change how our component looks based on that. 有时候,基于某些来自组件视图*外部*的条件应用样式是很有用的。 比如,在文档的``元素上可能有一个用于表示样式主题(Theme)的CSS类,而我们应当基于它来决定组件的样式。 - + Use the `:host-context()` pseudo-class selector. It works just like the function form of `:host()`. It looks for a CSS class in *any ancestor* of the component host element, all the way up to the document root. It's useful when combined with another selector. 这时可以使用`:host-context()`伪类选择器。它也以类似`:host()`形式使用。它在当前组件宿主元素的*祖先节点*中查找CSS类, 直到文档的根节点为止。在与其它选择器组合使用时,它非常有用。 - + In the following example, we apply a `background-color` style to all `

` elements *inside* the component, only if some ancestor element has the CSS class `theme-light`. 在下面的例子中,只有当某个祖先元素有CSS类`theme-light`时,我们才会把`background-color`样式应用到组件*内部*的所有`

`元素中。 - + +makeExample('component-styles/ts/app/hero-details.component.css', 'hostcontext')(format='.') :marked ### /deep/ ### /deep/ - + Component styles normally apply only to the HTML in the component's own template. “组件样式”通常只会作用于组件自身的HTML上。 - + We can use the `/deep/` selector to force a style down through the child component tree into all the child component views. The `/deep/` selector works to any depth of nested components, and it applies *both to the view children and the content children* of the component. 我们可以使用`/deep/`选择器,来强制一个样式对各级子组件的视图也生效,它*不但作用于组件的子视图,也会作用于组件的内容*。 - - In this example, we target all `

` elements, from the host element down + + In this example, we target all `

` elements, from the host element down through this component to all of its child elements in the DOM: - + 在这个例子中,我们以所有的`

`元素为目标,从宿主元素到当前元素再到DOM中的所有子元素: +makeExample('component-styles/ts/app/hero-details.component.css', 'deep')(format=".") :marked The `/deep/` selector also has the alias `>>>`. We can use either of the two interchangeably. - + `/deep/`选择器还有一个别名`>>>`。我们可以任意交替使用它们。 .alert.is-important @@ -193,7 +193,7 @@ a(id="special-selectors") This is the default and it is what we use most of the time. See the [Controlling View Encapsulation](#view-encapsulation) section for more details. - + `/deep/`和`>>>`选择器只能被用在**仿真(Emulated)**模式下。 这种方式是默认值,也是用得最多的方式。要了解更多,请参阅[控制视图包装模式](#view-encapsulation)一节。 @@ -202,9 +202,9 @@ a(id='loading-styles') :marked ## Loading Styles into Components ## 把样式加载进组件中 - + We have several ways to add styles to a component: - + 我们有几种方式来把样式加入组件: * inline in the template HTML * 内联在模板的HTML中 @@ -212,42 +212,42 @@ a(id='loading-styles') * 设置`styles`或`styleUrls`元数据 * with CSS imports * 通过CSS文件导入 - + The scoping rules outlined above apply to each of these loading patterns. 上述局限化规则对所有这些加载模式都适用。 - + ### Styles in Metadata ### 元数据中的样式 - + We can add a `styles` #{_array} property to the `@Component` #{_decorator}. Each string in the #{_array} (usually just one string) defines the CSS. 我们可以给`@Component`#{_decoratorCn}添加一个`styles`数组型属性。 这个数组中的每一个字符串(通常也只有一个)定义一份CSS。 - + +makeExample('component-styles/ts/app/hero-app.component.ts') :marked ### Template Inline Styles ### 模板内联样式 - + We can embed styles directly into the HTML template by putting them inside `