diff --git a/public/docs/_examples/dependency-injection/ts/app/heroes/hero.service.2.ts b/public/docs/_examples/dependency-injection/ts/app/heroes/hero.service.2.ts
index 7060373fbf..d0737bb611 100644
--- a/public/docs/_examples/dependency-injection/ts/app/heroes/hero.service.2.ts
+++ b/public/docs/_examples/dependency-injection/ts/app/heroes/hero.service.2.ts
@@ -7,7 +7,9 @@ import {Logger} from '../logger.service';
@Injectable()
export class HeroService {
+ //#docregion ctor
constructor(private _logger: Logger) { }
+ //#enddocregion ctor
getHeroes() {
this._logger.log('Getting heroes ...')
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 ebc8bfe162..de698f70e9 100644
--- a/public/docs/_examples/dependency-injection/ts/app/providers.component.ts
+++ b/public/docs/_examples/dependency-injection/ts/app/providers.component.ts
@@ -1,4 +1,5 @@
// Examples of provider arrays
+//#docplaster
import { Component, Host, Inject, Injectable,
provide, Provider} from 'angular2/core';
@@ -103,10 +104,8 @@ class EvenBetterLogger {
template: template,
providers:
//#docregion providers-5
- [
- UserService,
- provide(Logger, {useClass: EvenBetterLogger})
- ]
+ [ UserService,
+ provide(Logger, {useClass: EvenBetterLogger}) ]
//#enddocregion providers-5
})
export class ProviderComponent5 {
@@ -131,11 +130,9 @@ class OldLogger {
template: template,
providers:
//#docregion providers-6a
- [
- NewLogger,
+ [ NewLogger,
// Not aliased! Creates two instances of `NewLogger`
- provide(OldLogger, {useClass:NewLogger})
- ]
+ provide(OldLogger, {useClass:NewLogger}) ]
//#enddocregion providers-6a
})
export class ProviderComponent6a {
@@ -156,11 +153,9 @@ export class ProviderComponent6a {
template: template,
providers:
//#docregion providers-6b
- [
- NewLogger,
+ [ NewLogger,
// Alias OldLogger w/ reference to NewLogger
- provide(OldLogger, {useExisting: NewLogger})
- ]
+ provide(OldLogger, {useExisting: NewLogger}) ]
//#enddocregion providers-6b
})
export class ProviderComponent6b {
@@ -306,7 +301,9 @@ export class ProviderComponent10b {
log: (msg:string)=> this._logger.logs.push(msg),
logs: []
}
+ // #enddocregion provider-10-logger
this._logger.log("Optional logger was not available.")
+ // #docregion provider-10-logger
}
// #enddocregion provider-10-logger
else {
diff --git a/public/docs/ts/latest/guide/dependency-injection.jade b/public/docs/ts/latest/guide/dependency-injection.jade
index e180df003d..043eb12006 100644
--- a/public/docs/ts/latest/guide/dependency-injection.jade
+++ b/public/docs/ts/latest/guide/dependency-injection.jade
@@ -399,38 +399,41 @@ include ../../../../_includes/_util-fns
We're likely to need the same logger service everywhere in our application
so we put it at the root level of the application in the `app/` folder and
we register it in the `providers` array of the metadata for our application root component, `AppComponent`.
-+makeExample('dependency-injection/ts/app/providers.component.ts','providers-1', 'app/app.component.ts (providers)')
++makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger', 'app/app.component.ts (providers)')
:marked
- If we forget to register it, Angular will throw an exception when it first needs the logger:
+ If we forget to register it, Angular throws an exception when it first looks for the logger:
code-example(format).
EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger)
:marked
- That's telling us that the dependency injector couldn't find the *provider* for the logger
- when it first tried to call the logger — inside the `HeroService` when the `HeroListComponent`
- was calling for heroes.
- The *provider* is the subject of our next section.
+ That's Angular telling us that the dependency injector couldn't find the *provider* for the logger.
+ It needed that provider to create a `Logger` to inject into a new `HeroService` which it needed to
+ create and inject into a new `HeroListComponent`.
+
+ The chain of creations started with the `Logger` provider. The *provider* is the subject of our next section.
But wait! What if the logger is optional?
### Optional dependencies
Our `HeroService` currently requires a `Logger`. What if we could get by without a logger?
- We'd use it if we had it, ignore it if we didn't.
+ We'd use it if we had it, ignore it if we didn't. We can do that.
- First we import the `@Optional` decorator.
+ First import the `@Optional()` decorator.
+makeExample('dependency-injection/ts/app/providers.component.ts','import-optional')(format='.')
:marked
- Then rewrite the constructor with that decorator to make the logger optional.
+ Then rewrite the constructor with `@Optional()` decorator preceding the private `_logger` parameter.
+ That tells the injector that `_logger` is optional.
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-ctor')(format='.')
:marked
Be prepared for a null logger. If we don't register one somewhere up the line,
- the injector will inject `null`. We have a method that logs. What can we do?
+ the injector will inject `null`. We have a method that logs.
+ What can we do to avoid a null reference exception?
- We could substitute a *do-nothing* logger stub so that our methods continue to work:
+ We could substitute a *do-nothing* logger stub so that calling methods continue to work:
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-logger')(format='.')
:marked
Obviously we'd take a more sophisticated approach if the logger were optional
- elsewhere as well.
+ in multiple locations.
But enough about optional loggers. In our sample application, the `Logger` is required.
We must register a `Logger` with the application injector using *providers*
@@ -445,18 +448,15 @@ code-example(format).
The injector relies on **providers** to create instances of the services
that it injects into components and other services.
- We must register *providers* with the injector or it won't know what to do.
+ We must register a service *provider* with the injector or it won't know how to create the service.
Earlier we registered the `Logger` service in the `providers` array of the metadata for the `AppComponent` like this:
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger')
:marked
- The `providers` array appears to hold service classes (one service class in this example).
- In reality it holds instances of the [Provider](../api/core/Provider-class.html) class.
-
- In our example, when the `HeroService` constructor specifies `logger:Logger`, it's expecting
- something that has the shape and behavior of the `Logger` class.
-
- There are many ways to *provide* something that has the shape and behavior of a `Logger`.
+ The `providers` array appears to hold a service class.
+ In reality it holds an instance the [Provider](../api/core/Provider-class.html) class that can create that service.
+
+ There are many ways to *provide* something that looks and behaves like a `Logger`.
The `Logger` class itself is an obvious and natural provider - it has the right shape and it's designed to be created.
But it's not the only way.
@@ -511,7 +511,7 @@ code-example(format).
### Aliased Class Providers
Suppose there is an old component that depends upon an `OldLogger` class.
- `OldLogger` has the same interface as the `NewLogger` but, for some reason,
+ `OldLogger` has the same interface as the `NewLogger` but for some reason
we can't update the old component to use it.
When the *old* component logs a message with `OldLogger`,
@@ -545,14 +545,15 @@ code-example(format).
Sometimes we need to create the dependent value dynamically,
based on information we won't have until the last possible moment.
- Maybe the information can change.
+ Maybe the information keeps changes repeatedly in the course of the browser session..
Suppose also that the injectable service has no independent access to the source of this information.
- This situation calls for a **factory provider** as we illustrate next.
+ This situation calls for a **factory provider**.
- Our HeroService should hide *secret* heroes from normal users.
- Only authorized users should see them.
+ Let's illustrate by adding a new business requirement:
+ the HeroService must hide *secret* heroes from normal users.
+ Only authorized users should see secret heroes.
Like the `EvenBetterLogger`, the `HeroService` needs a fact about the user.
It needs to know if the user is authorized to see secret heroes.
@@ -560,6 +561,8 @@ code-example(format).
as when we log in a different user.
Unlike `EvenBetterLogger`, we can't inject the `UserService` into the `HeroService`.
+ The `HeroService` won't have direct access to the user information to decide
+ who is authoriazed and who is not.
.l-sub-section
:marked
Why? We don't know either. Stuff like this happens.
@@ -573,9 +576,9 @@ code-example(format).
A factory provider needs a factory function:
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','factory', 'app/heroes/hero.service.provider.ts (factory)')(format='.')
:marked
- Although our `HeroService` knows nothing about the `UserService`, our factory function does.
+ Although the `HeroService` has no access to the `UserService`, our factory function does.
- We inject both the `Logger` and the `UserService` into the factory provider:
+ We inject both the `Logger` and the `UserService` into the factory provider and let the injector pass them along to the factory function:
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (provider)')(format='.')
:marked
@@ -586,12 +589,15 @@ code-example(format).
The `deps` property is an array of [provider tokens](#token).
The `Logger` and `UserService` classes serve as tokens for their own class providers.
+ The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
:marked
- Notice that we've captured the factory provider in an exported variable, `heroServiceProvider`.
- This extra step makes it easier for us to register our `HeroService` whereever we need it.
+ Notice that we captured the factory provider in an exported variable, `heroServiceProvider`.
+ This extra step makes the factory provider re-usable.
+ We can register our `HeroService` with this variable whereever we need it.
In our sample, we need it only in the `HeroesComponent`
- where it replaces the previous `HeroService` registration in the metadata `providers` array:
+ where it replaces the previous `HeroService` registration in the metadata `providers` array.
+ Here we see the new and the old implementation side-by-side:
+makeTabs(
`dependency-injection/ts/app/heroes/heroes.component.ts,
dependency-injection/ts/app/heroes/heroes.component.1.ts`,
@@ -606,11 +612,11 @@ code-example(format).
When we register a provider with an injector we associate that provider with a dependency injection token.
The injector maintains an internal *token/provider* map that it references when
- asked for a dependency
+ asked for a dependency. The token is the key to the map.
In all previous examples, the dependency value has been a class *instance* and
- the class *type* served as its own lookup token.
- Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token.
+ the class *type* served as its own lookup key.
+ Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the key/token.
+makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.')
:marked
We have similar good fortune (in typescript) when we write a constructor that requires an injected class-based dependency.
@@ -618,7 +624,7 @@ code-example(format).
service associated with that `HeroService` class token:
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-8-ctor')(format=".")
:marked
- This is all especially convenient when we consider that most dependency values are provided by classes.
+ This is especially convenient when we consider that most dependency values are provided by classes.
### Non-class Dependencies
@@ -626,7 +632,7 @@ code-example(format).
Sometimes the thing we want to inject is a string, a function, or an object.
Applications often define configuration objects with lots of small facts like the title of the application or the address of a web api endpoint.
- These configuration objects aren't always instances of a class. They're just objects ... like this one:
+ These configuration objects aren't always instances of a class. They tend to be object hashes like this one:
+makeExample('dependency-injection/ts/app/app.config.ts','config','app/app-config.ts')(format='.')
:marked
@@ -638,13 +644,19 @@ code-example(format).
// begin Typescript only
:marked
+ ### Interfaces aren't valid tokens
+
The `CONFIG` constant has an interface, `Config`. Unfortunately, we
- **cannot use an interface as a token**
+ cannot use an interface as a token:
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9a-interface')(format=".")
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9a-ctor-interface')(format=".")
:marked
- It's not Angular's fault. An interface is a TypeScript design-time artifact.
- It disappears from the generated JavaScript so there is no interface type information for Angular to find at runtime.
+ That seems strange if we're used to dependency injection in strongly-typed languages where
+ an interface is the preferred dependency lookup key.
+
+ It's not Angular's fault. An interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces.
+ The TypeScript interface disappears from the generated JavaScript.
+ There is no interface type information left for Angular to find at runtime.
// end Typescript only
:marked
@@ -684,7 +696,9 @@ code-example(format).
.l-sub-section
:marked
- Angular uses `OpaqueTokens` to register all of its non-class dependencies.
+ Angular itself uses `OpaqueTokens` to register all of its own non-class dependencies. For example,
+ [HTTP_PROVIDERS](https://angular.io/docs/ts/latest/api/http/HTTP_PROVIDERS-let.html)
+ is the `OpaqueToken` associated with an array of providers for persisting data with the Angular `Http` client.
Internally, the `Provider` turns both the string and the class type into an `OpaqueToken`
and keys its *token/provider* map with that.
@@ -709,8 +723,13 @@ code-example(format).
+makeExample('dependency-injection/ts/app/injector.component.ts', 'injector',
'app/injector.component.ts')
:marked
- Angular injects the component's own `Injector` which the component uses to acquire services.
- The services themselves are not injected. They're retrieved via the injector.
+ The `Injector` is itself an injectable service.
+
+ In this example, Angular injects the component's own `Injector` into the component's constructor.
+ The component then asks the injected injector for the services it wants.
+
+ Note that the services themselves are not injected into the component.
+ They are retrieved by calling `injector.get`.
The `get` method throws an error if it can't resolve the requested service.
We can call `getOptional` instead, which we do in one case
@@ -718,14 +737,15 @@ code-example(format).
.l-sub-section
:marked
- This technique is an example of the [Service Locator Pattern](https://en.wikipedia.org/wiki/Service_locator_pattern).
+ The technique we just described is an example of the
+ [Service Locator Pattern](https://en.wikipedia.org/wiki/Service_locator_pattern).
We **avoid** this technique unless we genuinely need it.
It encourages a careless grab-bag approach such as we see here.
It's difficult to explain, understand, and test.
We can't know by inspecting the constructor what this class requires or what it will do.
- It could acquire services from any ancestor component, not just its own.
- We're forced to spelunk the implementation and hope for the best.
+ It could acquire services from any ancestor component, not just its own.
+ We're forced to spelunk the implementation to discover what it does.
Framework developers may take this approach when they
must acquire services generically and dynamically.