docs(di-cookbook): InjectionToken->OpaqueToken; clarify class-interface
This commit is contained in:
parent
f38620d882
commit
ebd8b48da6
|
@ -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)); }
|
||||
|
|
|
@ -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 {}
|
|
@ -0,0 +1,9 @@
|
|||
<h3>{{title}}</h3>
|
||||
<div>Winner: <strong>{{heroOfTheMonth.name}}</strong></div>
|
||||
<div>Reason for award: <strong>{{heroOfTheMonth.description}}</strong></div>
|
||||
<div>Runners-up: <strong id="rups1">{{runnersUp}}</strong></div>
|
||||
|
||||
<p>Logs:</p>
|
||||
<div id="logs">
|
||||
<div *ngFor="let log of logs">{{log}}</div>
|
||||
</div>
|
|
@ -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<string>('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 = `
|
||||
<h3>{{title}}</h3>
|
||||
<div>Winner: <strong>{{heroOfTheMonth.name}}</strong></div>
|
||||
<div>Reason for award: <strong>{{heroOfTheMonth.description}}</strong></div>
|
||||
<div>Runners-up: <strong id="rups1">{{runnersUp}}</strong></div>
|
||||
|
||||
<p>Logs:</p>
|
||||
<div id="logs">
|
||||
<div *ngFor="let log of logs">{{log}}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// #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 {
|
||||
|
|
|
@ -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;
|
|
@ -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<string>('RunnersUp');
|
||||
// #enddocregion runners-up
|
||||
|
||||
// #docregion factory-synopsis
|
||||
|
|
|
@ -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<AppConfig>('app.config');
|
||||
// #enddocregion token
|
||||
|
||||
// #docregion config
|
||||
|
|
|
@ -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 <live-example name="cb-dependency-injection"></live-example>
|
||||
|
@ -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,21 +482,27 @@ 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.
|
||||
|
||||
+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger','src/app/date-logger.service.ts (MinimalLogger)')(format='.')
|
||||
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/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`.
|
||||
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
|
||||
figure.image-display
|
||||
img(src="/resources/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px")
|
||||
|
||||
.l-main-section
|
||||
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 <a href="../api/core/index/OpaqueToken-class.html"><b>OpaqueToken</b></a>.
|
||||
The definition looks like this:
|
||||
to define and use an <a href="../api/core/index/InjectionToken-class.html"><b>InjectionToken</b></a>.
|
||||
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=".")
|
||||
|
||||
|
|
Loading…
Reference in New Issue