From ebd8b48da6e290497bcbf90e3ef4cb79c92041bb Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Sat, 1 Apr 2017 00:08:46 -0700 Subject: [PATCH] docs(di-cookbook): InjectionToken->OpaqueToken; clarify class-interface --- .../ts/src/app/date-logger.service.ts | 21 +-- .../src/app/hero-of-the-month.component.1.ts | 26 +++ .../src/app/hero-of-the-month.component.html | 9 + .../ts/src/app/hero-of-the-month.component.ts | 34 ++-- .../ts/src/app/minimal-logger.service.ts | 22 +++ .../ts/src/app/runners-up.ts | 4 +- .../ts/src/app/app.component.ts | 2 +- .../ts/src/app/app.config.ts | 4 +- .../latest/cookbook/dependency-injection.jade | 155 ++++++++++-------- public/docs/ts/latest/glossary.jade | 2 +- .../ts/latest/guide/dependency-injection.jade | 18 +- 11 files changed, 170 insertions(+), 127 deletions(-) create mode 100644 public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.1.ts create mode 100644 public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.html create mode 100644 public/docs/_examples/cb-dependency-injection/ts/src/app/minimal-logger.service.ts diff --git a/public/docs/_examples/cb-dependency-injection/ts/src/app/date-logger.service.ts b/public/docs/_examples/cb-dependency-injection/ts/src/app/date-logger.service.ts index e5b597db02..875779be40 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/src/app/date-logger.service.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/src/app/date-logger.service.ts @@ -4,29 +4,10 @@ import { Injectable } from '@angular/core'; import { LoggerService } from './logger.service'; -// #docregion minimal-logger -// class used as a restricting interface (hides other public members) -export abstract class MinimalLogger { - logInfo: (msg: string) => void; - logs: string[]; -} -// #enddocregion minimal-logger - -/* -// Transpiles to: -// #docregion minimal-logger-transpiled - var MinimalLogger = (function () { - function MinimalLogger() {} - return MinimalLogger; - }()); - exports("MinimalLogger", MinimalLogger); -// #enddocregion minimal-logger-transpiled - */ - // #docregion date-logger-service @Injectable() // #docregion date-logger-service-signature -export class DateLoggerService extends LoggerService implements MinimalLogger +export class DateLoggerService extends LoggerService // #enddocregion date-logger-service-signature { logInfo(msg: any) { super.logInfo(stamp(msg)); } diff --git a/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.1.ts b/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.1.ts new file mode 100644 index 0000000000..da2c57aeaf --- /dev/null +++ b/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.1.ts @@ -0,0 +1,26 @@ +// Illustrative (not used), mini-version of the actual HeroOfTheMonthComponent +// Injecting with the MinimalLogger "interface-class" +import { Component, NgModule } from '@angular/core'; +import { LoggerService } from './logger.service'; +import { MinimalLogger } from './minimal-logger.service'; + +// #docregion +@Component({ + selector: 'hero-of-the-month', + templateUrl: './hero-of-the-month.component.html', + // Todo: move this aliasing, `useExisting` provider to the AppModule + providers: [{ provide: MinimalLogger, useExisting: LoggerService }] +}) +export class HeroOfTheMonthComponent { + logs: string[] = []; + constructor(logger: MinimalLogger) { + logger.logInfo('starting up'); + } +} +// #enddocregion + +// This NgModule exists only to avoid the Angular language service's "undeclared component" error +@NgModule({ + declarations: [ HeroOfTheMonthComponent ] +}) +class NoopModule {} diff --git a/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.html b/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.html new file mode 100644 index 0000000000..f0ae619d6a --- /dev/null +++ b/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.html @@ -0,0 +1,9 @@ +

{{title}}

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

Logs:

+
+
{{log}}
+
diff --git a/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts b/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts index c5bafca36d..90ad13d639 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts @@ -1,19 +1,19 @@ /* tslint:disable:one-line:check-open-brace*/ // #docplaster -// #docregion opaque-token -import { OpaqueToken } from '@angular/core'; +// #docregion injection-token +import { InjectionToken } from '@angular/core'; -export const TITLE = new OpaqueToken('title'); -// #enddocregion opaque-token +export const TITLE = new InjectionToken('title'); +// #enddocregion injection-token // #docregion hero-of-the-month import { Component, Inject } from '@angular/core'; -import { DateLoggerService, - MinimalLogger } from './date-logger.service'; +import { DateLoggerService } from './date-logger.service'; import { Hero } from './hero'; import { HeroService } from './hero.service'; import { LoggerService } from './logger.service'; +import { MinimalLogger } from './minimal-logger.service'; import { RUNNERS_UP, runnersUpFactory } from './runners-up'; @@ -22,28 +22,16 @@ import { RUNNERS_UP, 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', - template: template, + templateUrl: './hero-of-the-month.component.html', providers: [ // #docregion use-value { provide: Hero, useValue: someHero }, - // #docregion provide-opaque-token + // #docregion provide-injection-token { provide: TITLE, useValue: 'Hero of the Month' }, - // #enddocregion provide-opaque-token + // #enddocregion provide-injection-token // #enddocregion use-value // #docregion use-class { provide: HeroService, useClass: HeroService }, @@ -52,9 +40,9 @@ const template = ` // #docregion use-existing { provide: MinimalLogger, useExisting: LoggerService }, // #enddocregion use-existing - // #docregion provide-opaque-token, use-factory + // #docregion provide-injection-token, use-factory { provide: RUNNERS_UP, useFactory: runnersUpFactory(2), deps: [Hero, HeroService] } - // #enddocregion provide-opaque-token, use-factory + // #enddocregion provide-injection-token, use-factory ] }) export class HeroOfTheMonthComponent { diff --git a/public/docs/_examples/cb-dependency-injection/ts/src/app/minimal-logger.service.ts b/public/docs/_examples/cb-dependency-injection/ts/src/app/minimal-logger.service.ts new file mode 100644 index 0000000000..d87fa594d1 --- /dev/null +++ b/public/docs/_examples/cb-dependency-injection/ts/src/app/minimal-logger.service.ts @@ -0,0 +1,22 @@ +// #docregion +// Class used as a "narrowing" interface that exposes a minimal logger +// Other members of the actual implementation are invisible +export abstract class MinimalLogger { + logs: string[]; + logInfo: (msg: string) => void; +} +// #enddocregion + +/* +// Transpiles to: +// #docregion minimal-logger-transpiled + var MinimalLogger = (function () { + function MinimalLogger() {} + return MinimalLogger; + }()); + exports("MinimalLogger", MinimalLogger); +// #enddocregion minimal-logger-transpiled +*/ + +// See http://stackoverflow.com/questions/43154832/unexpected-token-export-in-angular-app-with-systemjs-and-typescript/ +export const _ = 0; diff --git a/public/docs/_examples/cb-dependency-injection/ts/src/app/runners-up.ts b/public/docs/_examples/cb-dependency-injection/ts/src/app/runners-up.ts index 6eb8b195d0..0ce81ca55c 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/src/app/runners-up.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/src/app/runners-up.ts @@ -1,12 +1,12 @@ // #docplaster // #docregion -import { OpaqueToken } from '@angular/core'; +import { InjectionToken } from '@angular/core'; import { Hero } from './hero'; import { HeroService } from './hero.service'; // #docregion runners-up -export const RUNNERS_UP = new OpaqueToken('RunnersUp'); +export const RUNNERS_UP = new InjectionToken('RunnersUp'); // #enddocregion runners-up // #docregion factory-synopsis diff --git a/public/docs/_examples/dependency-injection/ts/src/app/app.component.ts b/public/docs/_examples/dependency-injection/ts/src/app/app.component.ts index dd83963a4b..3563fdc070 100644 --- a/public/docs/_examples/dependency-injection/ts/src/app/app.component.ts +++ b/public/docs/_examples/dependency-injection/ts/src/app/app.component.ts @@ -3,7 +3,7 @@ // #docregion imports import { Component, Inject } from '@angular/core'; -import { APP_CONFIG, AppConfig } from './app.config'; +import { APP_CONFIG, AppConfig } from './app.config'; import { Logger } from './logger.service'; import { UserService } from './user.service'; // #enddocregion imports diff --git a/public/docs/_examples/dependency-injection/ts/src/app/app.config.ts b/public/docs/_examples/dependency-injection/ts/src/app/app.config.ts index 7626c46e5b..f224e6e1ab 100644 --- a/public/docs/_examples/dependency-injection/ts/src/app/app.config.ts +++ b/public/docs/_examples/dependency-injection/ts/src/app/app.config.ts @@ -1,7 +1,7 @@ // #docregion token -import { OpaqueToken } from '@angular/core'; +import { InjectionToken } from '@angular/core'; -export let APP_CONFIG = new OpaqueToken('app.config'); +export let APP_CONFIG = new InjectionToken('app.config'); // #enddocregion token // #docregion config diff --git a/public/docs/ts/latest/cookbook/dependency-injection.jade b/public/docs/ts/latest/cookbook/dependency-injection.jade index eaf3745ab5..e29174b83f 100644 --- a/public/docs/ts/latest/cookbook/dependency-injection.jade +++ b/public/docs/ts/latest/cookbook/dependency-injection.jade @@ -8,34 +8,44 @@ include ../_util-fns :marked # Contents - - [Application-wide dependencies](#app-wide-dependencies) - - [External module configuration](#external-module-configuration) - - [`@Injectable()` and nested service dependencies](#nested-dependencies) - - [`@Injectable()`](#injectable-1) - - [Limit service scope to a component subtree](#service-scope) - - [Multiple service instances (sandboxing)](#multiple-service-instances) - - [Qualify dependency lookup with `@Optional()` and `@Host()`](#qualify-dependency-lookup) - - [Demonstration](#demonstration) - - [Inject the component's DOM element](#component-element) - - [Define dependencies with providers](#providers) - - [Defining providers](#defining-providers) - - [The *provide* object literal](#provide) - - [`useValue`—the *value provider*](#usevalue) - - [`useClass`—the *class provider*](#useclass) - - [`useExisting`—the *alias provider*](#useexisting) - - [`useFactory`—the *factory provider*](#usefactory) - - [Provider token alternatives: the class-interface and `OpaqueToken`](#tokens) - - [class-interface](#class-interface) - - [`OpaqueToken`](#opaque-token) - - [Inject into a derived class](#di-inheritance) - - [Find a parent component by injection](#find-parent) - - [Find parent with a known component type](#known-parent) - - [Cannot find a parent by its base class](#base-parent) - - [Find a parent by its class-interface](#class-interface-parent) - - [Find a parent in a tree of parents with `@SkipSelf()`](#parent-tree) - - [The `Parent` class-interface](#parent-token) - - [A `provideParent()` helper function](#provideparent) - - [Break circularities with a forward class reference (*forwardRef*)](#forwardref) + * [Application-wide dependencies](#app-wide-dependencies) + * [External module configuration](#external-module-configuration) + * [`@Injectable()` and nested service dependencies](#nested-dependencies) + + * [`@Injectable()`](#injectable-1) + + * [Limit service scope to a component subtree](#service-scope) + * [Multiple service instances (sandboxing)](#multiple-service-instances) + * [Qualify dependency lookup with `@Optional()` and `@Host()`](#qualify-dependency-lookup) + + * [Demonstration](#demonstration) + + * [Inject the component's DOM element](#component-element) + * [Define dependencies with providers](#providers) + + * [Defining providers](#defining-providers) + * [The *provide* object literal](#provide) + * [`useValue`—the *value provider*](#usevalue) + * [`useClass`—the *class provider*](#useclass) + * [`useExisting`—the *alias provider*](#useexisting) + * [`useFactory`—the *factory provider*](#usefactory) + + * [Provider token alternatives: the class-interface and `InjectionToken`](#tokens) + + * [class-interface](#class-interface) + * [`InjectionToken`](#injection-token) + + * [Inject into a derived class](#di-inheritance) + * [Find a parent component by injection](#find-parent) + + * [Find parent with a known component type](#known-parent) + * [Cannot find a parent by its base class](#base-parent) + * [Find a parent by its class-interface](#class-interface-parent) + * [Find a parent in a tree of parents with `@SkipSelf()`](#parent-tree) + * [The `Parent` class-interface](#parent-token) + * [A `provideParent()` helper function](#provideparent) + + * [Break circularities with a forward class reference (*forwardRef*)](#forwardref) :marked See the @@ -385,11 +395,11 @@ a#defining-providers You need other ways to deliver dependency values and that means you need other ways to specify a provider. The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them. - + It's visually simple: a few properties and the logs produced by a logger. 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 you plenty to think about. + The code behind it gives you plenty to think about. +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','hero-of-the-month','hero-of-the-month.component.ts') .l-main-section @@ -400,15 +410,14 @@ a(id='provide') 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). - The *definition* object has one main property, `useValue`, that indicates how the provider - should create or return the provided value. + The *definition* object has a required property that specifies how to create the singleton instance of the service. In this case, the property. .l-main-section a(id='usevalue') :marked #### useValue—the *value provider* - Set the `useValue` property to a ***fixed value*** that the provider can return as the dependency object. + Set the `useValue` property to a ***fixed value*** that the provider can return as the service instance (AKA, the "dependency object"). Use this technique to provide *runtime configuration constants* such as website base addresses and feature flags. You can use a *value provider* in a unit test to replace a production service with a fake or mock. @@ -422,8 +431,9 @@ a(id='usevalue') and the consumer of the injected hero would want the type information. The `TITLE` provider token is *not a class*. - It's a special kind of provider lookup key called an [OpaqueToken](#opaquetoken). - You can use an `OpaqueToken` when the dependency is a simple value like a string, a number, or a function. + It's a special kind of provider lookup key called an [InjectionToken](#injection-token). + You can use an `InjectionToken` for any kind of provider but it's particular + helpful when the dependency is a simple value like a string, a number, or a function. The value of a *value provider* must be defined *now*. You can't create the value later. Obviously the title string literal is immediately available. @@ -447,7 +457,7 @@ a(id='useclass') +makeExample('cb-dependency-injection/ts/src/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. + class to be created (`HeroService`) is also the provider's dependency injection token. It's in this long form to de-mystify the preferred short form. The second provider substitutes the `DateLoggerService` for the `LoggerService`. @@ -472,22 +482,28 @@ a(id='useexisting') +makeExample('cb-dependency-injection/ts/src/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. - This example shows aliasing for that very purpose here. - Imagine that the `LoggerService` had a large API; it's actually only three methods and a property. - You'd want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](#class-interface): + The following example shows aliasing for that purpose. + + Imagine that the `LoggerService` had a large API, much larger than the actual three methods and a property. + You might want to shrink that API surface to just the members you actually need. + Here the `MinimalLogger` [*class-interface*](#class-interface) reduces the API to two members: -+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger','src/app/date-logger.service.ts (MinimalLogger)')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/minimal-logger.service.ts', null,'src/app/minimal-logger.service.ts')(format='.') :marked - The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript: + Now put it to use in a simplified version of the `HeroOfTheMonthComponent`. ++makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.1.ts', null,'src/app/hero-of-the-month.component.ts (minimal version)')(format='.') +:marked + The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger` so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor: figure.image-display img(src="/resources/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API") :marked - Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService` - which happens to be the `DateLoggerService`. This is because of the override provider - registered previously via `useClass`. - The following image, which displays the logging date, confirms the point: -figure.image-display - img(src="/resources/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px") + Behind the scenes, Angular actually sets the `logger` parameter to the full service registered under the `LoggingService` token which happens to be the `DateLoggerService` that was [provided above](#useclass). + +.l-sub-section + :marked + The following image, which displays the logging date, confirms the point: + figure.image-display + img(src="/resources/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px") .l-main-section a(id='usefactory') @@ -533,7 +549,7 @@ a(id='usefactory') a(id="tokens") .l-main-section :marked - ## Provider token alternatives: the *class-interface* and *OpaqueToken* + ## Provider token alternatives: the *class-interface* and *InjectionToken* Angular dependency injection is easiest when the provider *token* is a class that is also the type of the returned dependency object, or what you usually call the *service*. @@ -550,29 +566,26 @@ a(id="tokens") +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-existing') :marked The `MinimalLogger` is an abstract class. -+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/minimal-logger.service.ts')(format='.') :marked You usually inherit from an abstract class. - But `LoggerService` doesn't inherit from `MinimalLogger`. *No class* inherits from it. - Instead, you use it like an interface. + But *no class* in this application inherits from `MinimalLogger`. - Look again at the declaration for `DateLoggerService`: -+makeExample('cb-dependency-injection/ts/src/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*. + The `LoggerService` and the `DateLoggerService` _could_ have inherited from `MinimalLogger`. + They could have _implemented_ it instead in the manner of an interface. + But they did neither. + The `MinimalLogger` is used exclusively as a dependency injection token. When you use a class this way, it's called a ***class-interface***. The key benefit of a *class-interface* is that you can get the strong-typing of an interface - and you can ***use it as a provider token*** in the same manner as a normal class. + and you can ***use it as a provider token*** in the way you would a normal class. A ***class-interface*** should define *only* the members that its consumers are allowed to call. Such a narrowing interface helps decouple the concrete class from its consumers. - The `MinimalLogger` defines just two of the `LoggerClass` members. .l-sub-section :marked - #### Why *MinimalLogger* is a class and not an interface + #### Why *MinimalLogger* is a class and not a TypeScript interface You can't use an interface as a provider token because interfaces are not JavaScript objects. They exist only in the TypeScript design space. @@ -581,17 +594,17 @@ a(id="tokens") A provider token must be a real JavaScript object of some kind: such as a function, an object, a string, or a class. - Using a class as an interface gives you the characteristics of an interface in a JavaScript object. + Using a class as an interface gives you the characteristics of an interface in a real JavaScript object. - To minimize memory cost, the class should have *no implementation*. - The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript: - +makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger-transpiled')(format='.') + Of course a real object occupies memory. To minimize memory cost, the class should have *no implementation*. + The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function: + +makeExample('cb-dependency-injection/ts/src/app/minimal-logger.service.ts','minimal-logger-transpiled')(format='.') :marked - It never grows larger no matter how many members you add *as long as they are typed but not implemented*. + Notice that it doesn't have a single member. It never grows no matter how many members you add to the class *as long as those members are typed but not implemented*. Look again at the TypeScript `MinimalLogger` class to confirm that it has no implementation. -a(id='opaque-token') +a(id='injection-token') :marked - ### _OpaqueToken_ + ### _InjectionToken_ Dependency objects can be simple values like dates, numbers and strings, or shapeless objects like arrays and functions. @@ -601,14 +614,16 @@ a(id='opaque-token') a JavaScript object that has a friendly name but won't conflict with another token that happens to have the same name. - The `OpaqueToken` has these characteristics. + The `InjectionToken` has these characteristics. You encountered them twice in the *Hero of the Month* example, in the *title* value provider and in the *runnersUp* factory provider. -+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','provide-opaque-token')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','provide-injection-token')(format='.') :marked You created the `TITLE` token like this: -+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','opaque-token')(format='.') - ++makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','injection-token')(format='.') +:marked + The type parameter, while optional, conveys the dependency's type to developers and tooling. + The token description is another developer aid. a(id="di-inheritance") .l-main-section diff --git a/public/docs/ts/latest/glossary.jade b/public/docs/ts/latest/glossary.jade index 3893135f3a..c68e1bf970 100644 --- a/public/docs/ts/latest/glossary.jade +++ b/public/docs/ts/latest/glossary.jade @@ -251,7 +251,7 @@ a#decoration At the core, an [`injector`](#injector) returns dependency values on request. The expression `injector.get(token)` returns the value associated with the given token. - A token is an Angular type (`OpaqueToken`). You rarely need to work with tokens directly; most + A token is an Angular type (`InjectionToken`). You rarely need to work with tokens directly; most methods accept a class name (`Foo`) or a string ("foo") and Angular converts it to a token. When you write `injector.get(Foo)`, the injector returns the value associated with the token for the `Foo` class, typically an instance of `Foo` itself. diff --git a/public/docs/ts/latest/guide/dependency-injection.jade b/public/docs/ts/latest/guide/dependency-injection.jade index df143f187d..4e7dc692c2 100644 --- a/public/docs/ts/latest/guide/dependency-injection.jade +++ b/public/docs/ts/latest/guide/dependency-injection.jade @@ -39,7 +39,7 @@ block includes * [Dependency injection tokens](#dependency-injection-tokens) * [Non-class dependencies](#non-class-dependencies) - * [`OpaqueToken`](#opaquetoken) + * [`InjectionToken`](#injection-token) * [Optional dependencies](#optional) * [Summary](#summary) @@ -645,7 +645,7 @@ a#value-provider :marked See more `useValue` examples in the [Non-class dependencies](#non-class-dependencies) and - [OpaqueToken](#opaquetoken) sections. + [InjectionToken](#injection-token) sections. #factory-provider :marked @@ -784,18 +784,20 @@ p The TypeScript interface disappears from the generated JavaScript. There is no interface type information left for Angular to find at runtime. -a#opaquetoken +a#injection-token :marked - ### _OpaqueToken_ + ### _InjectionToken_ One solution to choosing a provider token for non-class dependencies is - to define and use an OpaqueToken. - The definition looks like this: + to define and use an InjectionToken. + The definition of such a token looks like this: +makeExample('dependency-injection/ts/src/app/app.config.ts','token')(format='.') - :marked - Register the dependency provider using the `OpaqueToken` object: + The type parameter, while optional, conveys the dependency's type to developers and tooling. + The token description is another developer aid. + + Register the dependency provider using the `InjectionToken` object: +makeExample('dependency-injection/ts/src/app/providers.component.ts','providers-9')(format=".")