915 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			915 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| include ../_util-fns
 | |
| 
 | |
| // #docregion intro
 | |
| :marked
 | |
|   **Dependency injection** is an important application design pattern.
 | |
|   Angular has its own dependency injection framework, and
 | |
|   we really can't build an Angular application without it.
 | |
|   It's used so widely that almost everyone just calls it _DI_.
 | |
| 
 | |
|   In this chapter we'll learn what DI is and why we want it.
 | |
|   Then we'll learn [how to use it](#angular-di) in an Angular app.
 | |
| // #enddocregion intro
 | |
| :marked
 | |
|   [Run the live example](/resources/live-examples/dependency-injection/ts/plnkr.html)
 | |
| // #docregion why-1
 | |
| <a id="why-di"></a>
 | |
| .l-main-section
 | |
| :marked
 | |
|   ## Why dependency injection?
 | |
| 
 | |
|   Let's start with the following code.
 | |
| // #enddocregion why-1
 | |
| +makeExample('dependency-injection/ts/app/car/car-no-di.ts', 'car', 'app/car/car.ts (without DI)')
 | |
| // #docregion why-2
 | |
| - var lang = current.path[1]
 | |
| - var prefix = lang == 'dart' ? '' : 'this.'
 | |
| :marked
 | |
|   Our `Car` creates everything it needs inside its constructor.
 | |
|   What's the problem?
 | |
| 
 | |
|   The problem is that our `Car` class is brittle, inflexible, and hard to test.
 | |
| 
 | |
|   Our `Car` needs an engine and tires. Instead of asking for them,
 | |
|   the `Car` constructor creates its own copies by "new-ing" them from
 | |
|   the very specific classes, `Engine` and `Tires`.
 | |
| 
 | |
|   What if the `Engine` class evolves and its constructor requires a parameter?
 | |
|   Our `Car` is broken and stays broken until we rewrite it along the lines of
 | |
|   `#{prefix}engine = new Engine(theNewParameter)`.
 | |
|   We didn't care about `Engine` constructor parameters when we first wrote `Car`.
 | |
|   We don't really care about them now.
 | |
|   But we'll *have* to start caring because
 | |
|   when the definion of `Engine` changes, our `Car` class must change.
 | |
|   That makes `Car` brittle.
 | |
| 
 | |
|   What if we want to put a different brand of tires on our `Car`? Too bad.
 | |
|   We're locked into whatever brand the `Tires` class creates. That makes our `Car` inflexible.
 | |
| 
 | |
|   Right now each new car gets its own engine. It can't share an engine with other cars.
 | |
|   While that makes sense for an automobile engine,
 | |
|   we can think of other dependencies that should be shared, such as the onboard
 | |
|   wireless connection to the manufacturer's service center. Our `Car` lacks the flexibility
 | |
|   to share services that have been created previously for other consumers.
 | |
| 
 | |
|   When we write tests for our `Car` we're at the mercy of its hidden dependencies.
 | |
|   Is it even possible to create a new `Engine` in a test environment?
 | |
|   What does `Engine`itself depend upon? What does that dependency depend on?
 | |
|   Will a new instance of `Engine` make an asynchronous call to the server?
 | |
|   We certainly don't want that going on during our tests.
 | |
| 
 | |
|   What if our `Car` should flash a warning signal when tire pressure is low?
 | |
|   How do we confirm that it actually does flash a warning
 | |
|   if we can't swap in low-pressure tires during the test?
 | |
| 
 | |
|   We have no control over the car's hidden dependencies.
 | |
|   When we can't control the dependencies, a class becomes difficult to test.
 | |
| 
 | |
|   How can we make `Car` more robust, flexible, and testable?
 | |
| 
 | |
|   That's super easy. We change our `Car` constructor to a version with DI:
 | |
| 
 | |
|   <a id="ctor-injection"></a>
 | |
| // #enddocregion why-2
 | |
| +makeTabs(
 | |
|   'dependency-injection/ts/app/car/car.ts, dependency-injection/ts/app/car/car-no-di.ts',
 | |
|   'car-ctor, car-ctor',
 | |
|   'app/car/car.ts (excerpt with DI), app/car/car.ts (excerpt without DI)')(format=".")
 | |
| // #docregion why-3-1
 | |
| :marked
 | |
|   See what happened? We moved the definition of the dependencies to the constructor.
 | |
|   Our `Car` class no longer creates an engine or tires.
 | |
|   It just consumes them.
 | |
| // #enddocregion why-3-1
 | |
| // TypeScript only
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     We also leverage TypeScript's constructor syntax for declaring parameters and properties simultaneously.
 | |
| // #docregion why-3-2
 | |
| :marked
 | |
|   Now we create a car by passing the engine and tires to the constructor.
 | |
| // #enddocregion why-3-2
 | |
| - var stylePattern = { otl: /(new Car.*$)/gm };
 | |
| +makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation', '', stylePattern)(format=".")
 | |
| // #docregion why-4
 | |
| :marked
 | |
|   How cool is that?
 | |
|   The definition of the engine and tire dependencies are
 | |
|   decoupled from the `Car` class itself.
 | |
|   We can pass in any kind of engine or tires we like, as long as they
 | |
|   conform to the general API requirements of an engine or tires.
 | |
| 
 | |
|   If someone extends the `Engine` class, that is not `Car`'s problem.
 | |
| // #enddocregion why-4
 | |
| // Must copy the following, due to indented +make.
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
 | |
|     something like this:
 | |
| 
 | |
|   - var stylePattern = { otl: /(new Car.*$)/gm };
 | |
|   +makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-param', '', stylePattern)(format=".")
 | |
| 
 | |
|   :marked
 | |
|     The critical point is this: `Car` itself did not have to change.
 | |
|     We'll take care of the consumer's problem soon enough.
 | |
| // #docregion why-6
 | |
| :marked
 | |
|   The `Car` class is much easier to test because we are in complete control
 | |
|   of its dependencies.
 | |
|   We can pass mocks to the constructor that do exactly what we want them to do
 | |
|   during each test:
 | |
| // #enddocregion why-6
 | |
| - var stylePattern = { otl: /(new Car.*$)/gm };
 | |
| +makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-mocks', '', stylePattern)(format=".")
 | |
| // #docregion why-7
 | |
| :marked
 | |
|   **We just learned what dependency injection is**.
 | |
| 
 | |
|   It's a coding pattern in which a class receives its dependencies from external
 | |
|   sources rather than creating them itself.
 | |
| 
 | |
|   Cool! But what about that poor consumer?
 | |
|   Anyone who wants a `Car` must now
 | |
|   create all three parts: the `Car`, `Engine`, and `Tires`.
 | |
|   The `Car` class shed its problems at the consumer's expense.
 | |
|   We need something that takes care of assembling these parts for us.
 | |
| 
 | |
|   We could write a giant class to do that:
 | |
| // #enddocregion why-7
 | |
| +makeExample('dependency-injection/ts/app/car/car-factory.ts', null, 'app/car/car-factory.ts')
 | |
| // #docregion why-8
 | |
| :marked
 | |
|   It's not so bad now with only three creation methods.
 | |
|   But maintaining it will be hairy as the application grows.
 | |
|   This factory is going to become a huge spiderweb of
 | |
|   interdependent factory methods!
 | |
| 
 | |
|   Wouldn't it be nice if we could simply list the things we want to build without
 | |
|   having to define which dependency gets injected into what?
 | |
| 
 | |
|   This is where the dependency injection framework comes into play.
 | |
|   Imagine the framework had something called an _injector_.
 | |
|   We register some classes with this injector, and it figures out how to create them.
 | |
| 
 | |
|   When we need a `Car`, we simply ask the injector to get it for us and we're good to go.
 | |
| // #enddocregion why-8
 | |
| +makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-call')(format=".")
 | |
| // #docregion why-9
 | |
| :marked
 | |
|   Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`.
 | |
|   The consumer knows nothing about creating a `Car`.
 | |
|   We don't have a gigantic factory class to maintain.
 | |
|   Both `Car` and consumer simply ask for what they need and the injector delivers.
 | |
| 
 | |
|   This is what a **dependency injection framework** is all about.
 | |
| 
 | |
|   Now that we know what dependency injection is and appreciate its benefits,
 | |
|   let's see how it is implemented in Angular.
 | |
| // #enddocregion why-9
 | |
| // #docregion di-1
 | |
| <a id="angular-di"></a>
 | |
| .l-main-section
 | |
| :marked
 | |
|   ## Angular dependency injection
 | |
| 
 | |
|   Angular ships with its own dependency injection framework. This framework can also be used
 | |
|   as a standalone module by other applications and frameworks.
 | |
| 
 | |
|   That sounds nice. What does it do for us when building components in Angular?
 | |
|   Let's see, one step at a time.
 | |
| 
 | |
|   We'll begin with a simplified version of the `HeroesComponent`
 | |
|   that we built in the [The Tour of Heroes](../tutorial/).
 | |
| // #enddocregion di-1
 | |
| +makeTabs(
 | |
|   `dependency-injection/ts/app/heroes/heroes.component.1.ts,
 | |
|   dependency-injection/ts/app/heroes/hero-list.component.1.ts,
 | |
|   dependency-injection/ts/app/heroes/hero.ts,
 | |
|   dependency-injection/ts/app/heroes/mock-heroes.ts`,
 | |
|   'v1,,,',
 | |
|   `app/heroes/heroes.component.ts,
 | |
|   app/heroes/hero-list.component.ts,
 | |
|   app/heroes/hero.ts,
 | |
|   app/heroes/mock-heroes.ts`)
 | |
| // #docregion di-2
 | |
| :marked
 | |
|   The `HeroesComponent` is the root component of the *Heroes* feature area.
 | |
|   It governs all the child components of this area.
 | |
|   Our stripped down version has only one child, `HeroListComponent`,
 | |
|   which displays a list of heroes.
 | |
| // #enddocregion di-2
 | |
| // #docregion di-3
 | |
| :marked
 | |
|   Right now `HeroListComponent` gets heroes from `HEROES`, an in-memory collection
 | |
|   defined in another file.
 | |
|   That may suffice in the early stages of development, but it's far from ideal.
 | |
|   As soon as we try to test this component or want to get our heroes data from a remote server,
 | |
|   we'll have to change the implementation of `heroes` and
 | |
|   fix every other use of the `HEROES` mock data.
 | |
| 
 | |
|   Let's make a service that hides how we get hero data.
 | |
| // #enddocregion di-3
 | |
| 
 | |
| // Unnecessary for Dart
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     Write this service in its own file. See [this note](#forward-ref) to understand why.
 | |
| +makeExample('dependency-injection/ts/app/heroes/hero.service.1.ts',null, 'app/heroes/hero.service.ts' )
 | |
| // #docregion di-4
 | |
| :marked
 | |
|   Our `HeroService` exposes a `getHeroes` method that returns
 | |
|   the same mock data as before, but none of its consumers need to know that.
 | |
| // #enddocregion di-4
 | |
| // #docregion di-5
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     We aren't even pretending this is a real service.
 | |
|     If we were actually getting data from a remote server, the API would have to be asynchronous,
 | |
|     perhaps returning
 | |
|     [ES2015 promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
 | |
|     We'd also have to rewrite the way components consume our service.
 | |
|     This is important in general, but not to our current story.
 | |
| // #enddocregion di-5
 | |
| // #docregion di-6
 | |
| :marked
 | |
|   A service is nothing more than a class in Angular 2.
 | |
|   It remains nothing more than a class until we register it with an Angular injector.
 | |
| // #enddocregion di-6
 | |
| // #docregion di-configure-injector-1
 | |
| :marked
 | |
|   ### Configuring the injector
 | |
| 
 | |
|   <a id="bootstrap"></a>
 | |
|   We don't have to create an Angular injector.
 | |
|   Angular creates an application-wide injector for us during the bootstrap process.
 | |
| // #enddocregion di-configure-injector-1
 | |
| +makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (excerpt)')(format='.')
 | |
| // #docregion di-configure-injector-2
 | |
| :marked
 | |
|   We do have to configure the injector by registering the **providers**
 | |
|   that create the services our application requires.
 | |
|   We'll explain what [providers](#providers) are later in this chapter.
 | |
|   Before we do, let's see an example of provider registration during bootstrapping:
 | |
| // #enddocregion di-configure-injector-2
 | |
| +makeExample('dependency-injection/ts/app/main.1.ts', 'bootstrap')(format='.')
 | |
| // #docregion di-configure-injector-3
 | |
| :marked
 | |
|   The injector now knows about our `HeroService`.
 | |
|   An instance of our `HeroService` will be available for injection across our entire application.
 | |
| 
 | |
|   Of course we can't help wondering about that comment telling us not to do it this way.
 | |
|   It *will* work. It's just not a best practice.
 | |
|   The bootstrap provider option is intended for configuring and overriding Angular's own
 | |
|   preregistered services, such as its routing support.
 | |
| 
 | |
|   The preferred approach is to register application providers in application components.
 | |
|   Because the `HeroService` is used within the *Heroes* feature area —
 | |
|   and nowhere else — the ideal place to register it is in the top-level `HeroesComponent`.
 | |
| // #enddocregion di-configure-injector-3
 | |
| // #docregion di-register-providers-1
 | |
| :marked
 | |
|   ### Registering providers in a component
 | |
|   Here's a revised `HeroesComponent` that registers the `HeroService`.
 | |
| // #enddocregion di-register-providers-1
 | |
| +makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts',null,'app/heroes/heroes.component.ts')
 | |
| // #docregion di-register-providers-2
 | |
| :marked
 | |
|   Look closely at the `providers` part of the `@Component` metadata:
 | |
| // #enddocregion di-register-providers-2
 | |
| +makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts','providers')(format='.')
 | |
| // #docregion di-register-providers-3
 | |
| :marked
 | |
|   An instance of the `HeroService` is now available for injection in this `HeroesComponent`
 | |
|   and all of its child components.
 | |
| 
 | |
|   The `HeroesComponent` itself doesn't happen to need the `HeroService`.
 | |
|   But its child `HeroListComponent` does, so we head there next.
 | |
| // #enddocregion di-register-providers-3
 | |
| // #docregion di-prepare-for-injection-1
 | |
| :marked
 | |
|   ### Preparing the HeroListComponent for injection
 | |
| 
 | |
|   The `HeroListComponent` should get heroes from the injected `HeroService`.
 | |
|   Per the dependency injection pattern, the component must ask for the service in its constructor, [as we explained
 | |
|   earlier](#ctor-injection).
 | |
|   It's a small change:
 | |
| // #enddocregion di-prepare-for-injection-1
 | |
| +makeTabs(
 | |
|   `dependency-injection/ts/app/heroes/hero-list.component.2.ts,
 | |
|   dependency-injection/ts/app/heroes/hero-list.component.1.ts`,
 | |
|   null,
 | |
|   `app/heroes/hero-list.component (with DI),
 | |
|    app/heroes/hero-list.component (without DI)`)
 | |
| // Must copy the following, due to indented +make.
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     ### Focus on the constructor
 | |
| 
 | |
|     Adding a parameter to the constructor isn't all that's happening here.
 | |
| 
 | |
|   +makeExample('dependency-injection/ts/app/heroes/hero-list.component.2.ts', 'ctor')(format=".")
 | |
| 
 | |
|   // TypeScript only
 | |
|   :marked
 | |
|     We're writing in TypeScript and have followed the parameter name with a type annotation, `:HeroService`.
 | |
|     The class is also decorated with the `@Component` decorator (scroll up to confirm that fact).
 | |
| 
 | |
|     When the TypeScript compiler evaluates this class, it sees the `@Component` decorator and adds class metadata
 | |
|     into the generated JavaScript code. Within that metadata lurks the information that
 | |
|     associates the `heroService` parameter with the `HeroService` class.
 | |
| 
 | |
|     That's how the Angular injector knows to inject an instance of the `HeroService` when it
 | |
|     creates a new `HeroListComponent`.
 | |
| // #docregion di-create-injector-implicitly-1
 | |
| :marked
 | |
|   <a id="di-metadata"></a>
 | |
|   ### Creating the injector (implicitly)
 | |
|   When we introduced the idea of an injector above, we showed how to create
 | |
|   an injector and use it to create a new `Car`.
 | |
| // #enddocregion di-create-injector-implicitly-1
 | |
| +makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-create-and-call')(format=".")
 | |
| // #docregion di-create-injector-implicitly-2
 | |
| :marked
 | |
|   We won't find code like that in the Tour of Heroes or any of our other samples.
 | |
|   We *could* write [code with an explicit injector](#explicit-injector) if we *had* to, but we rarely do.
 | |
|   Angular takes care of creating and calling injectors
 | |
|   when it creates components for us — whether through HTML markup, as in `<hero-list></hero-list>`,
 | |
|   or after navigating to a component with the [router](./router.html).
 | |
|   If we let Angular do its job, we'll enjoy the benefits of automated dependency injection.
 | |
| // #enddocregion di-create-injector-implicitly-2
 | |
| // #docregion di-singleton-services
 | |
| :marked
 | |
|   ### Singleton services
 | |
|   Dependencies are singletons within the scope of an injector.
 | |
|   In our example, a single `HeroService` instance is shared among the
 | |
|   `HeroesComponent` and its `HeroListComponent` children.
 | |
| 
 | |
|   However, Angular DI is an hierarchical injection
 | |
|   system, which means that nested injectors can create their own service instances.
 | |
|   Learn more about that in the [Hierarchical Injectors](./hierarchical-dependency-injection.html) chapter.
 | |
| // #enddocregion di-singleton-services
 | |
| 
 | |
| // Skip this for Dart, for now
 | |
| // #docregion di-testing-component-1
 | |
| :marked
 | |
|   ### Testing the component
 | |
|   We emphasized earlier that designing a class for dependency injection makes the class easier to test.
 | |
|   Listing dependencies as constructor parameters may be all we need to test application parts effectively.
 | |
| 
 | |
|   For example, we can create a new `HeroListComponent` with a mock service that we can manipulate
 | |
|   under test:
 | |
| // #enddocregion di-testing-component-1
 | |
| +makeExample('dependency-injection/ts/app/test.component.ts', 'spec')(format='.')
 | |
| // #docregion di-testing-component-2
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     Learn more in [Testing](../testing/index.html).
 | |
| // #enddocregion di-testing-component-2
 | |
| // #docregion di-service-service-1
 | |
| :marked
 | |
|   ### When the service needs a service
 | |
|   Our `HeroService` is very simple. It doesn't have any dependencies of its own.
 | |
| 
 | |
| 
 | |
|   What if it had a dependency? What if it reported its activities through a logging service?
 | |
|   We'd apply the same *constructor injection* pattern,
 | |
|   adding a constructor that takes a `Logger` parameter.
 | |
| 
 | |
|   Here is the revision compared to the original.
 | |
| // #enddocregion di-service-service-1
 | |
| +makeTabs(
 | |
|   `dependency-injection/ts/app/heroes/hero.service.2.ts,
 | |
|   dependency-injection/ts/app/heroes/hero.service.1.ts`,
 | |
|   null,
 | |
|   `app/heroes/hero.service (v.2),
 | |
|   app/heroes/hero.service (v.1)`)
 | |
| // #docregion di-service-service-2
 | |
| :marked
 | |
|   The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `logger`.
 | |
|   We call that property within our `getHeroes` method when anyone asks for heroes.
 | |
| // #enddocregion di-service-service-2
 | |
| // #docregion di-injectable-1
 | |
| - var lang = current.path[1]
 | |
| - var decoration = lang == 'dart' ? 'annotation' : 'decoration'
 | |
| - var tsmetadata = lang == 'ts' ? 'As <a href="#di-metadata">we mentioned earlier</a>, <b>TypeScript only generates metadata for classes that have a decorator.</b>' : ''
 | |
| :marked
 | |
|   <a id="injectable"></a>
 | |
|   ### Why @Injectable?
 | |
|   Notice the `@Injectable()` #{decoration} above the service class.
 | |
|   We haven't seen `@Injectable()` before.
 | |
|   As it happens, we could have added it to our first version of `HeroService`.
 | |
|   We didn't bother because we didn't need it then.
 | |
| 
 | |
|   We need it now... now that our service has an injected dependency.
 | |
|   We need it because Angular requires constructor parameter metadata in order to inject a `Logger`. !{tsmetadata}
 | |
| // #enddocregion di-injectable-1
 | |
| 
 | |
| // #docregion di-injectable-2
 | |
| - var lang = current.path[1]
 | |
| - var a_decorator = lang == 'dart' ? 'an annotation' : 'a decorator'
 | |
| - var decorated = lang == 'dart' ? 'annotated' : 'decorated'
 | |
| - var any_decorator = lang == 'dart' ? '' : 'TypeScript generates metadata for any class with a decorator, and any decorator will do.'
 | |
| .callout.is-helpful
 | |
|   header Suggestion: add @Injectable() to every service class
 | |
|   :marked
 | |
|     We recommend adding `@Injectable()` to every service class, even those that don't have dependencies
 | |
|     and, therefore, do not technically require it. Here's why:
 | |
|   ul(style="font-size:inherit")
 | |
|     li <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when we add a dependency later.
 | |
|     li <b>Consistency:</b> All services follow the same rules, and we don't have to wonder why #{a_decorator} is missing.
 | |
| 
 | |
|   :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.
 | |
| 
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     The `HeroesComponent` has an injected dependency too. Why don't we add `@Injectable()` to the `HeroesComponent`?
 | |
| 
 | |
|     We *can* add it if we really want to. It isn't necessary because
 | |
|     the `HeroesComponent` is already #{decorated} with `@Component`. #{any_decorator}
 | |
| // #enddocregion di-injectable-2
 | |
| .callout.is-critical
 | |
|   header Always include the parentheses
 | |
|   :marked
 | |
|     Always use `@Injectable()`, not just `@Injectable`.
 | |
|     Our application will fail mysteriously if we forget the parentheses.
 | |
| 
 | |
| // #docregion logger-service-1
 | |
| .l-main-section
 | |
| :marked
 | |
|   ## Creating and registering a logger service
 | |
|   We're injecting a logger into our `HeroService` in two steps:
 | |
|   1. Create the logger service.
 | |
|   1. Register it with the application.
 | |
| 
 | |
|   The logger service implementation is no big deal.
 | |
| // #enddocregion logger-service-1
 | |
| +makeExample(
 | |
|   'dependency-injection/ts/app/logger.service.ts',null, 'app/logger.service')
 | |
| 
 | |
| // Copied into Dart, due to different directory structure
 | |
| :marked
 | |
|   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-logger', 'app/app.component.ts (excerpt)')
 | |
| // #docregion logger-service-3
 | |
| :marked
 | |
|   If we forget to register the logger, Angular throws an exception when it first looks for the logger:
 | |
| code-example(format, language="html").
 | |
|   EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger)
 | |
| // #enddocregion logger-service-3
 | |
| // #docregion logger-service-4
 | |
| :marked
 | |
|   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?
 | |
|   <a id="optional"></a>
 | |
|   ### 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 can do that.
 | |
| // #enddocregion logger-service-4
 | |
| 
 | |
| // TypeScript only?
 | |
| :marked
 | |
|   First import the `@Optional()` decorator.
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','import-optional')(format='.')
 | |
| 
 | |
| // #docregion logger-service-5
 | |
| - var lang = current.path[1]
 | |
| - var rewrite = lang == 'dart' ? 'Just rewrite' : 'Then rewrite'
 | |
| - var decorator = lang == 'dart' ? 'annotation' : 'decorator'
 | |
| :marked
 | |
|   #{rewrite} the constructor with the `@Optional()` #{decorator} preceding the private `logger` parameter.
 | |
|   That tells the injector that `logger` is optional.
 | |
| // #enddocregion logger-service-5
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-ctor')(format='.')
 | |
| // #docregion logger-service-6
 | |
| :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 to avoid a null reference exception?
 | |
| 
 | |
|   We could substitute a *do-nothing* logger stub so that calling methods continue to work:
 | |
| // #enddocregion logger-service-6
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-logger')(format='.')
 | |
| // #docregion logger-service-7
 | |
| :marked
 | |
|   Obviously we'd take a more sophisticated approach if the logger were optional
 | |
|   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*,
 | |
|   as we learn in the next section.
 | |
| // #enddocregion logger-service-7
 | |
| 
 | |
| // #docregion providers-1
 | |
| :marked
 | |
| <a id="providers"></a>
 | |
| .l-main-section
 | |
| :marked
 | |
|   ## Injector providers
 | |
| 
 | |
|   A provider *provides* the concrete, runtime version of a dependency value.
 | |
|   The injector relies on **providers** to create instances of the services
 | |
|   that the injector injects into components and other services.
 | |
| 
 | |
|   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:
 | |
| // #enddocregion providers-1
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger')
 | |
| // #docregion providers-2
 | |
| - var lang = current.path[1]
 | |
| - var implements = lang == 'dart' ? 'implements' : 'looks and behaves like a '
 | |
| - var objectlike = lang == 'dart' ? '' : 'an object that behaves like '
 | |
| - var loggerlike = lang == 'dart' ? '' : 'We could provide a logger-like object. '
 | |
| :marked
 | |
|   The `providers` array appears to hold a service class.
 | |
|   In reality it holds an instance of the [Provider](../api/core/Provider-class.html) class that can create that service.
 | |
| 
 | |
|   There are many ways to *provide* something that #{implements} `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.
 | |
| 
 | |
|   We can configure the injector with alternative providers that can deliver #{objectlike} a `Logger`.
 | |
|   We could provide a substitute class. #{loggerlike}
 | |
|   We could give it a provider that calls a logger factory function.
 | |
|   Any of these approaches might be a good choice under the right circumstances.
 | |
| 
 | |
|   What matters is that the injector has a provider to go to when it needs a `Logger`.
 | |
| // #enddocregion providers-2
 | |
| // #docregion providers-provide-1
 | |
| :marked
 | |
|   <a id="provide"></a>
 | |
| // #enddocregion providers-provide-1
 | |
| 
 | |
| // Don't mention provide function in Dart
 | |
| :marked
 | |
|   ### The *Provider* class and *provide* function
 | |
| // #docregion providers-provide-1-1
 | |
| :marked
 | |
|   We wrote the `providers` array like this:
 | |
| // #enddocregion providers-provide-1-1
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-1')
 | |
| // #docregion providers-provide-2
 | |
| :marked
 | |
|   This is actually a short-hand expression for a provider registration that creates a new instance of the
 | |
|   [Provider](../api/core/Provider-class.html) class.
 | |
| // #enddocregion providers-provide-2
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-2')
 | |
| // #docregion providers-provide-3
 | |
| // Skip for Dart, where the provide() function won't pass type checking.
 | |
| :marked
 | |
|   The [provide](../api/core/provide-function.html) function is the more common, friendlier way to create a `Provider`:
 | |
| // #enddocregion providers-provide-3
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-3')
 | |
| // #docregion providers-provide-4-1
 | |
| // Modified for Dart.
 | |
| :marked
 | |
|   In both approaches — `Provider` class and `provide` function —
 | |
|   we supply two arguments.
 | |
| // #enddocregion providers-provide-4-1
 | |
| // #docregion providers-provide-4-2
 | |
| :marked
 | |
|   The first is the [token](#token) that serves as the key for both locating a dependency value
 | |
|   and registering the provider.
 | |
| // #enddocregion providers-provide-4-2
 | |
| 
 | |
| // Dart is different here (uses an optional parameter)
 | |
| :marked
 | |
|   The second is a provider definition object,
 | |
|   which we can think of as a *recipe* for creating the dependency value.
 | |
|   There are many ways to create dependency values... and many ways to write a recipe.
 | |
| // #docregion providers-alternative-1
 | |
| :marked
 | |
|   <a id="class-provider"></a>
 | |
|   ### Alternative class providers
 | |
| 
 | |
|   Occasionally we'll ask a different class to provide the service.
 | |
|   The following code tells the injector
 | |
|   to return a `BetterLogger` when something asks for the `Logger`.
 | |
| // #enddocregion providers-alternative-1
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-4')
 | |
| // #docregion providers-alternative-2
 | |
| :marked
 | |
|   ### Class provider with dependencies
 | |
|   Maybe an `EvenBetterLogger` could display the user name in the log message.
 | |
|   This logger gets the user from the injected `UserService`,
 | |
|   which happens also to be injected at the application level.
 | |
| // #enddocregion providers-alternative-2
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','EvenBetterLogger')
 | |
| // #docregion providers-alternative-3
 | |
| :marked
 | |
|   Configure it like we did `BetterLogger`.
 | |
| // #enddocregion providers-alternative-3
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-5')(format=".")
 | |
| // #docregion providers-aliased-1
 | |
| :marked
 | |
|   ### Aliased class providers
 | |
| 
 | |
|   Suppose an old component depends upon an `OldLogger` class.
 | |
|   `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`,
 | |
|   we want the singleton instance of `NewLogger` to handle it instead.
 | |
| 
 | |
|   The dependency injector should inject that singleton instance
 | |
|   when a component asks for either the new or the old logger.
 | |
|   The `OldLogger` should be an alias for `NewLogger`.
 | |
| 
 | |
|   We certainly do not want two different `NewLogger` instances in our app.
 | |
|   Unfortunately, that's what we get if we try to alias `OldLogger` to `NewLogger` with `useClass`.
 | |
| // #enddocregion providers-aliased-1
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-6a')(format=".")
 | |
| // #docregion providers-aliased-2
 | |
| :marked
 | |
|   The solution: Alias with the `useExisting` option.
 | |
| // #enddocregion providers-aliased-2
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-6b')(format=".")
 | |
| // #docregion providers-value-1
 | |
| <a id="value-provider"></a>
 | |
| :marked
 | |
|   ### Value providers
 | |
| // #enddocregion providers-value-1
 | |
| 
 | |
| // Typescript only
 | |
| :marked
 | |
|   Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','silent-logger')(format=".")
 | |
| :marked
 | |
|   Then we register a provider with the `useValue` option,
 | |
|   which makes this object play the logger role.
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-7')(format=".")
 | |
| 
 | |
| // #docregion providers-factory-1
 | |
| <a id="factory-provider"></a>
 | |
| :marked
 | |
|   ### Factory providers
 | |
| 
 | |
|   Sometimes we need to create the dependent value dynamically,
 | |
|   based on information we won't have until the last possible moment.
 | |
|   Maybe the information 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**.
 | |
| 
 | |
|   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.
 | |
|   That authorization can change during the course of a single application session,
 | |
|   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 authorized and who is not.
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     Why? We don't know either. Stuff like this happens.
 | |
| :marked
 | |
|   Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes.
 | |
| // #enddocregion providers-factory-1
 | |
| +makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (excerpt)')(format='.')
 | |
| // #docregion providers-factory-2
 | |
| :marked
 | |
|   We can inject the `Logger`, but we can't inject the  boolean `isAuthorized`.
 | |
|   We'll have to take over the creation of new instances of this `HeroService` with a factory provider.
 | |
| 
 | |
|   A factory provider needs a factory function:
 | |
| // #enddocregion providers-factory-2
 | |
| +makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','factory', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.')
 | |
| // #docregion providers-factory-3
 | |
| :marked
 | |
|   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 and let the injector pass them along to the factory function:
 | |
| // #enddocregion providers-factory-3
 | |
| +makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.')
 | |
| // #docregion providers-factory-4
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     The `useFactory` field tells Angular that the provider is a factory function
 | |
|     whose implementation is the `heroServiceFactory`.
 | |
| 
 | |
|     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.
 | |
| // #enddocregion providers-factory-4
 | |
| // #docregion providers-factory-5
 | |
| - var lang = current.path[1]
 | |
| - var anexportedvar = lang == 'dart' ? 'a constant' : 'an exported variable'
 | |
| - var variable = lang == 'dart' ? 'constant' : 'variable'
 | |
| :marked
 | |
|   Notice that we captured the factory provider in #{anexportedvar}, `heroServiceProvider`.
 | |
|   This extra step makes the factory provider reusable.
 | |
|   We can register our `HeroService` with this #{variable} wherever 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.
 | |
|   Here we see the new and the old implementation side-by-side:
 | |
| // #enddocregion providers-factory-5
 | |
| +makeTabs(
 | |
|   `dependency-injection/ts/app/heroes/heroes.component.ts,
 | |
|   dependency-injection/ts/app/heroes/heroes.component.1.ts`,
 | |
|   null,
 | |
|   `app/heroes/heroes.component (v.3),
 | |
|   app/heroes/heroes.component (v.2)`)
 | |
| 
 | |
| // #docregion tokens-1
 | |
| <a id="token"></a>
 | |
| .l-main-section
 | |
| :marked
 | |
|   ## Dependency injection tokens
 | |
| 
 | |
|   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. 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 key.
 | |
|   Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
 | |
| // #enddocregion tokens-1
 | |
| +makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.')
 | |
| // #docregion tokens-2
 | |
| :marked
 | |
|   We have similar good fortune when we write a constructor that requires an injected class-based dependency.
 | |
|   We define a constructor parameter with the `HeroService` class type,
 | |
|   and Angular knows to inject the
 | |
|   service associated with that `HeroService` class token:
 | |
| // #enddocregion tokens-2
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','provider-8-ctor')(format=".")
 | |
| // #docregion tokens-3
 | |
| :marked
 | |
|   This is especially convenient when we consider that most dependency values are provided by classes.
 | |
| // #enddocregion tokens-3
 | |
| 
 | |
| // #docregion tokens-non-class-deps-1
 | |
| - var lang = current.path[1]
 | |
| - var objectexamples = lang == 'dart' ? 'a string or list literal, or maybe a function' : 'a string, a function, or an object'
 | |
| // Is function injection useful? Should we show it?
 | |
| :marked
 | |
|   ### Non-class dependencies
 | |
| 
 | |
|   What if the dependency value isn't a class?
 | |
|   Sometimes the thing we want to inject is #{objectexamples}.
 | |
| // #enddocregion tokens-non-class-deps-1
 | |
| 
 | |
| // TS/JS only
 | |
| :marked
 | |
|   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 tend to be object hashes like this one:
 | |
| +makeExample('dependency-injection/ts/app/app.config.ts','config','app/app-config.ts (excerpt)')(format='.')
 | |
| 
 | |
| // TypeScript only?
 | |
| :marked
 | |
|   We'd like to make this `config` object available for injection.
 | |
|   We know we can register an object with a [value provider](#value-provider).
 | |
|   But what do we use for the token?
 | |
|   We don't have a class to serve as a token. There is no `Config` class.
 | |
| 
 | |
| // Typescript only
 | |
| <a id="interface"></a>
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     ### TypeScript interfaces aren't valid tokens
 | |
| 
 | |
|     The `CONFIG` constant has an interface, `Config`. Unfortunately, we
 | |
|     cannot use a TypeScript 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
 | |
|     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
 | |
| 
 | |
| // #docregion tokens-opaque-1
 | |
| <a id="opaque-token"></a>
 | |
| - var lang = current.path[1]
 | |
| - var opaquetoken = lang == 'dart' ? '<code>OpaqueToken</code>' : '<a href="../api/core/OpaqueToken-class.html"><code>OpaqueToken</code></a>'
 | |
| h3 OpaqueToken
 | |
| p.
 | |
|   The solution is to define and use an !{opaquetoken}.
 | |
|   The definition looks like this:
 | |
| // #enddocregion tokens-opaque-1
 | |
| +makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.')
 | |
| :marked
 | |
|   We register the dependency provider using the `OpaqueToken` object:
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','providers-9b')(format=".")
 | |
| // #docregion tokens-opaque-2
 | |
| - var lang = current.path[1]
 | |
| - var decorated = lang == 'dart' ? 'annotated' : 'decorated'
 | |
| - var configuration = lang == 'dart' ? '' : 'configuration'
 | |
| :marked
 | |
|   Now we can inject the #{configuration} object into any constructor that needs it, with
 | |
|   the help of an `@Inject` #{decorator} that tells Angular how to find the #{configuration} dependency value.
 | |
| // #enddocregion tokens-opaque-2
 | |
| +makeExample('dependency-injection/ts/app/providers.component.ts','provider-9b-ctor')(format=".")
 | |
| 
 | |
| // begin Typescript only
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     Although it plays no role in dependency injection,
 | |
|     the `Config` interface supports strong typing of the configuration object within the class.
 | |
| :marked
 | |
| // end typescript only
 | |
| 
 | |
| // Skip for Dart (we have another example)
 | |
| :marked
 | |
|   Or we can provide and inject the configuration object in our top-level `AppComponent`.
 | |
| +makeExample('dependency-injection/ts/app/app.component.ts','providers', 'app/app.component.ts (providers)')(format=".")
 | |
| +makeExample('dependency-injection/ts/app/app.component.ts','ctor', 'app/app.component.ts (constructor)')(format=".")
 | |
| 
 | |
| // #docregion summary
 | |
| .l-main-section
 | |
| :marked
 | |
|   ## Summary
 | |
|   We learned the basics of Angular dependency injection in this chapter.
 | |
|   We can register various kinds of providers,
 | |
|   and we know how to ask for an injected object (such as a service) by
 | |
|   adding a parameter to a constructor.
 | |
| 
 | |
|   Angular dependency injection is more capable than we've described.
 | |
|   We can learn more about its advanced features, beginning with its support for
 | |
|   nested injectors, in the
 | |
|   [Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter.
 | |
| // #enddocregion summary
 | |
| 
 | |
| // #docregion appendix-explicit-injector-1
 | |
| .l-main-section
 | |
| <a id="explicit-injector"></a>
 | |
| :marked
 | |
|   ### Appendix: Working with injectors directly
 | |
|   We rarely work directly with an injector.
 | |
|   Here's an `InjectorComponent` that does.
 | |
| // #enddocregion appendix-explicit-injector-1
 | |
| +makeExample('dependency-injection/ts/app/injector.component.ts', 'injector', 'app/injector.component.ts')
 | |
| // #docregion appendix-explicit-injector-2
 | |
| :marked
 | |
|   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 `get` with a second parameter (the value to return if the service is not found) 
 | |
|   instead, which we do in one case
 | |
|   to retrieve a service (`ROUS`) that isn't registered with this or any ancestor injector.
 | |
| 
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     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 to discover what it does.
 | |
| 
 | |
|     Framework developers may take this approach when they
 | |
|     must acquire services generically and dynamically.
 | |
| // #enddocregion appendix-explicit-injector-2
 | |
| 
 | |
| // TypeScript only? Unnecessary for Dart
 | |
| .l-main-section
 | |
| <a id="forward-ref"></a>
 | |
| :marked
 | |
|   ### Appendix: Why we recommend one class per file
 | |
| 
 | |
|   Having multiple classes in the same file is confusing and best avoided.
 | |
|   Developers expect one class per file. Keep them happy.
 | |
| 
 | |
|   If we scorn this advice and, say,
 | |
|   combine our `HeroService` class with the `HeroesComponent` in the same file,
 | |
|   **define the component last!**
 | |
|   If we define the component before the service,
 | |
|   we'll get a runtime null reference error.
 | |
| 
 | |
| .l-sub-section
 | |
|   :marked
 | |
|     We actually can define the component first with the help of the `forwardRef()` method as explained
 | |
|     in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html).
 | |
|     But why flirt with trouble?
 | |
|     Avoid the problem altogether by defining components and services in separate files.
 |