docs(DI-cookbook): copy edits and update TOCs

This commit is contained in:
Kapunahele Wong 2017-03-31 15:03:04 -04:00 committed by Ward Bell
parent c9db9b2648
commit f38620d882
1 changed files with 214 additions and 210 deletions

View File

@ -2,54 +2,44 @@ include ../_util-fns
:marked :marked
Dependency Injection is a powerful pattern for managing code dependencies. Dependency Injection is a powerful pattern for managing code dependencies.
In this cookbook we will explore many of the features of Dependency Injection (DI) in Angular. This cookbook explores many of the features of Dependency Injection (DI) in Angular.
<a id="toc"></a> <a id="toc"></a>
:marked :marked
## Table of contents # Contents
[Application-wide dependencies](#app-wide-dependencies) - [Application-wide dependencies](#app-wide-dependencies)
- [External module configuration](#external-module-configuration)
[External module configuration](#external-module-configuration) - [`@Injectable()` and nested service dependencies](#nested-dependencies)
- [`@Injectable()`](#injectable-1)
[*@Injectable* and nested service dependencies](#nested-dependencies) - [Limit service scope to a component subtree](#service-scope)
- [Multiple service instances (sandboxing)](#multiple-service-instances)
[Limit service scope to a component subtree](#service-scope) - [Qualify dependency lookup with `@Optional()` and `@Host()`](#qualify-dependency-lookup)
- [Demonstration](#demonstration)
[Multiple service instances (sandboxing)](#multiple-service-instances) - [Inject the component's DOM element](#component-element)
- [Define dependencies with providers](#providers)
[Qualify dependency lookup with *@Optional* and *@Host*](#qualify-dependency-lookup) - [Defining providers](#defining-providers)
- [The *provide* object literal](#provide)
[Inject the component's DOM element](#component-element) - [`useValue`&mdash;the *value provider*](#usevalue)
- [`useClass`&mdash;the *class provider*](#useclass)
[Define dependencies with providers](#providers) - [`useExisting`&mdash;the *alias provider*](#useexisting)
- [`useFactory`&mdash;the *factory provider*](#usefactory)
* [The *provide* object literal](#provide) - [Provider token alternatives: the class-interface and `OpaqueToken`](#tokens)
* [useValue - the *value provider*](#usevalue) - [class-interface](#class-interface)
* [useClass - the *class provider*](#useclass) - [`OpaqueToken`](#opaque-token)
* [useExisting - the *alias provider*](#useexisting) - [Inject into a derived class](#di-inheritance)
* [useFactory - the *factory provider*](#usefactory) - [Find a parent component by injection](#find-parent)
- [Find parent with a known component type](#known-parent)
[Provider token alternatives](#tokens) - [Cannot find a parent by its base class](#base-parent)
- [Find a parent by its class-interface](#class-interface-parent)
* [class-interface](#class-interface) - [Find a parent in a tree of parents with `@SkipSelf()`](#parent-tree)
* [OpaqueToken](#opaque-token) - [The `Parent` class-interface](#parent-token)
- [A `provideParent()` helper function](#provideparent)
[Inject into a derived class](#di-inheritance) - [Break circularities with a forward class reference (*forwardRef*)](#forwardref)
[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 (*@SkipSelf*)](#parent-tree)
* [A *provideParent* helper function](#provideparent)
[Break circularities with a forward class reference (*forwardRef*)](#forwardref)
:marked :marked
**See the <live-example name="cb-dependency-injection"></live-example>** See the <live-example name="cb-dependency-injection"></live-example>
of the code supporting this cookbook. of the code in this cookbook.
.l-main-section .l-main-section
@ -58,22 +48,23 @@ include ../_util-fns
## Application-wide dependencies ## Application-wide dependencies
Register providers for dependencies used throughout the application in the root application component, `AppComponent`. Register providers for dependencies used throughout the application in the root application component, `AppComponent`.
In the following example, we import and register several services The following example shows importing and registering
(the `LoggerService`, `UserContext`, and the `UserService`) the `LoggerService`, `UserContext`, and the `UserService`
in the `@Component` metadata `providers` array. in the `@Component` metadata `providers` array.
+makeExample('cb-dependency-injection/ts/src/app/app.component.ts','import-services','src/app/app.component.ts (excerpt)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/app.component.ts','import-services','src/app/app.component.ts (excerpt)')(format='.')
:marked :marked
All of these services are implemented as classes. All of these services are implemented as classes.
Service classes can act as their own providers which is why listing them in the `providers` array Service classes can act as their own providers which is why listing them in the `providers` array
is all the registration we need. is all the registration you need.
.l-sub-section .l-sub-section
:marked :marked
A *provider* is something that can create or deliver a service. A *provider* is something that can create or deliver a service.
Angular creates a service instance from a class provider by "new-ing" it. Angular creates a service instance from a class provider by using `new`.
Learn more about providers [below](#providers). Read more about providers in the [Dependency Injection](../guide/dependency-injection.html#!#injector-providers)
guide.
:marked :marked
Now that we've registered these services, Now that you've registered these services,
Angular can inject them into the constructor of *any* component or service, *anywhere* in the application. Angular can inject them into the constructor of *any* component or service, *anywhere* in the application.
+makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','ctor','src/app/hero-bios.component.ts (component constructor injection)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','ctor','src/app/hero-bios.component.ts (component constructor injection)')(format='.')
@ -83,12 +74,12 @@ include ../_util-fns
.l-main-section .l-main-section
:marked :marked
## External module configuration ## External module configuration
We often register providers in the `NgModule` rather than in the root application component. Generally, register providers in the `NgModule` rather than in the root application component.
We do this when (a) we expect the service to be injectable everywhere Do this when you expect the service to be injectable everywhere,
or (b) we must configure another application global service _before it starts_. or you are configuring another application global service _before the application starts_.
We see an example of the second case here, where we configure the Component Router with a non-default Here is an example of the second case, where the component router configuration includes a non-default
[location strategy](../guide/router.html#location-strategy) by listing its provider [location strategy](../guide/router.html#location-strategy) by listing its provider
in the `providers` list of the `AppModule`. in the `providers` list of the `AppModule`.
@ -98,37 +89,42 @@ a(id="injectable")
a(id="nested-dependencies") a(id="nested-dependencies")
.l-main-section .l-main-section
:marked :marked
## *@Injectable* and nested service dependencies ## _@Injectable()_ and nested service dependencies
The consumer of an injected service does not know how to create that service. The consumer of an injected service does not know how to create that service.
It shouldn't care. It shouldn't care.
It's the dependency injection's job to create and cache that service. It's the dependency injection's job to create and cache that service.
Sometimes a service depends on other services ... which may depend on yet other services. Sometimes a service depends on other services, which may depend on yet other services.
Resolving these nested dependencies in the correct order is also the framework's job. Resolving these nested dependencies in the correct order is also the framework's job.
At each step, the consumer of dependencies simply declares what it requires in its constructor and the framework takes over. At each step, the consumer of dependencies simply declares what it requires in its
constructor and the framework takes over.
For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`. The following example shows injecting both the `LoggerService` and the `UserContext` in the `AppComponent`.
+makeExample('cb-dependency-injection/ts/src/app/app.component.ts','ctor','src/app/app.component.ts')(format='.') +makeExample('cb-dependency-injection/ts/src/app/app.component.ts','ctor','src/app/app.component.ts')(format='.')
:marked :marked
The `UserContext` in turn has dependencies on both the `LoggerService` (again) and The `UserContext` in turn has its own dependencies on both the `LoggerService` and
a `UserService` that gathers information about a particular user. a `UserService` that gathers information about a particular user.
+makeExample('cb-dependency-injection/ts/src/app/user-context.service.ts','injectables','user-context.service.ts (injection)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/user-context.service.ts','injectables','user-context.service.ts (injection)')(format='.')
:marked :marked
When Angular creates an`AppComponent`, the dependency injection framework creates an instance of the `LoggerService` and When Angular creates the `AppComponent`, the dependency injection framework creates an instance of the `LoggerService` and
starts to create the `UserContextService`. starts to create the `UserContextService`.
The `UserContextService` needs the `LoggerService`, which the framework already has, and the `UserService`, which it has yet to create. The `UserContextService` needs the `LoggerService`, which the framework already has, and the `UserService`, which it has yet to create.
The `UserService` has no dependencies so the dependency injection framework can just `new` one into existence. The `UserService` has no dependencies so the dependency injection framework can just
use `new` to instantiate one.
The beauty of dependency injection is that the author of `AppComponent` didn't care about any of this. The beauty of dependency injection is that `AppComponent` doesn't care about any of this.
The author simply declared what was needed in the constructor (`LoggerService` and `UserContextService`) and the framework did the rest. You simply declare what is needed in the constructor (`LoggerService` and `UserContextService`)
and the framework does the rest.
Once all the dependencies are in place, the `AppComponent` displays the user information: Once all the dependencies are in place, the `AppComponent` displays the user information:
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User") img(src="/resources/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User")
a#injectable-1
:marked :marked
### *@Injectable()* ### *@Injectable()*
Notice the `@Injectable()`decorator on the `UserContextService` class. Notice the `@Injectable()`decorator on the `UserContextService` class.
@ -136,23 +132,24 @@ figure.image-display
:marked :marked
That decorator makes it possible for Angular to identify the types of its two dependencies, `LoggerService` and `UserService`. That decorator makes it possible for Angular to identify the types of its two dependencies, `LoggerService` and `UserService`.
Technically, the `@Injectable()`decorator is only _required_ for a service class that has _its own dependencies_. Technically, the `@Injectable()`decorator is only required for a service class that has _its own dependencies_.
The `LoggerService` doesn't depend on anything. The logger would work if we omitted `@Injectable()` The `LoggerService` doesn't depend on anything. The logger would work if you omitted `@Injectable()`
and the generated code would be slightly smaller. and the generated code would be slightly smaller.
But the service would break the moment we gave it a dependency and we'd have to go back But the service would break the moment you gave it a dependency and you'd have to go back
and add `@Injectable()` to fix it. We add `@Injectable()` from the start for the sake of consistency and to avoid future pain. and add `@Injectable()` to fix it. Add `@Injectable()` from the start for the sake
of consistency and to avoid future pain.
.alert.is-helpful .alert.is-helpful
:marked :marked
Although we recommend applying `@Injectable` to all service classes, do not feel bound by it. Although this site recommends applying `@Injectable()` to all service classes, don't feel bound by it.
Some developers prefer to add it only where needed and that's a reasonable policy too. Some developers prefer to add it only where needed and that's a reasonable policy too.
.l-sub-section .l-sub-section
:marked :marked
The `AppComponent` class had two dependencies as well but no `@Injectable()`. The `AppComponent` class had two dependencies as well but no `@Injectable()`.
It didn't need `@Injectable()` because that component class has the `@Component` decorator. It didn't need `@Injectable()` because that component class has the `@Component` decorator.
In Angular with TypeScript, a *single* decorator &mdash; *any* decorator &mdash; is sufficient to identify dependency types. In Angular with TypeScript, a *single* decorator&mdash;*any* decorator&mdash;is sufficient to identify dependency types.
<a id="service-scope"></a> <a id="service-scope"></a>
@ -161,10 +158,10 @@ figure.image-display
## Limit service scope to a component subtree ## Limit service scope to a component subtree
All injected service dependencies are singletons meaning that, All injected service dependencies are singletons meaning that,
for a given dependency injector ("injector"), there is only one instance of service. for a given dependency injector, there is only one instance of service.
But an Angular application has multiple dependency injectors, arranged in a tree hierarchy that parallels the component tree. But an Angular application has multiple dependency injectors, arranged in a tree hierarchy that parallels the component tree.
So a particular service can be *provided* (and created) at any component level and multiple times So a particular service can be *provided* and created at any component level and multiple times
if provided in multiple components. if provided in multiple components.
By default, a service dependency provided in one component is visible to all of its child components and By default, a service dependency provided in one component is visible to all of its child components and
@ -173,21 +170,23 @@ figure.image-display
Accordingly, dependencies provided in the root `AppComponent` can be injected into *any* component *anywhere* in the application. Accordingly, dependencies provided in the root `AppComponent` can be injected into *any* component *anywhere* in the application.
That isn't always desirable. That isn't always desirable.
Sometimes we want to restrict service availability to a particular region of the application. Sometimes you want to restrict service availability to a particular region of the application.
We can limit the scope of an injected service to a *branch* of the application hierarchy You can limit the scope of an injected service to a *branch* of the application hierarchy
by providing that service *at the sub-root component for that branch*. by providing that service *at the sub-root component for that branch*.
Here we provide the `HeroService` to the `HeroesBaseComponent` by listing it in the `providers` array: This example shows how similar providing a service to a sub-root component is
to providing a service in the root `AppComponent`. The syntax is the same.
Here, the `HeroService` is availble to the `HeroesBaseComponent` because it is in the `providers` array:
+makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','injection','src/app/sorted-heroes.component.ts (HeroesBaseComponent excerpt)') +makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','injection','src/app/sorted-heroes.component.ts (HeroesBaseComponent excerpt)')
:marked :marked
When Angular creates the `HeroesBaseComponent`, it also creates a new instance of `HeroService` When Angular creates the `HeroesBaseComponent`, it also creates a new instance of `HeroService`
that is visible only to the component and its children (if any). that is visible only to the component and its children, if any.
We could also provide the `HeroService` to a *different* component elsewhere in the application. You could also provide the `HeroService` to a *different* component elsewhere in the application.
That would result in a *different* instance of the service, living in a *different* injector. That would result in a *different* instance of the service, living in a *different* injector.
.l-sub-section .l-sub-section
:marked :marked
We examples of such scoped `HeroService` singletons appear throughout the accompanying sample code, Examples of such scoped `HeroService` singletons appear throughout the accompanying sample code,
including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`. including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`.
Each of these components has its own `HeroService` instance managing its own independent collection of heroes. Each of these components has its own `HeroService` instance managing its own independent collection of heroes.
@ -203,12 +202,12 @@ figure.image-display
:marked :marked
## Multiple service instances (sandboxing) ## Multiple service instances (sandboxing)
Sometimes we want multiple instances of a service at *the same level of the component hierarchy*. Sometimes you want multiple instances of a service at *the same level of the component hierarchy*.
A good example is a service that holds state for its companion component instance. A good example is a service that holds state for its companion component instance.
We need a separate instance of the service for each component. You need a separate instance of the service for each component.
Each service has its own work-state, isolated from the service-and-state of a different component. Each service has its own work-state, isolated from the service-and-state of a different component.
We call this *sandboxing* because each service and component instance has its own sandbox to play in. This is called *sandboxing* because each service and component instance has its own sandbox to play in.
<a id="hero-bios-component"></a> <a id="hero-bios-component"></a>
Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`. Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`.
@ -226,7 +225,7 @@ figure.image-display
+makeExample('cb-dependency-injection/ts/src/app/hero-bio.component.ts','component','src/app/hero-bio.component.ts') +makeExample('cb-dependency-injection/ts/src/app/hero-bio.component.ts','component','src/app/hero-bio.component.ts')
:marked :marked
The parent `HeroBiosComponent` binds a value to the `heroId`. The parent `HeroBiosComponent` binds a value to the `heroId`.
The `ngOnInit` pass that `id` to the service which fetches and caches the hero. The `ngOnInit` passes that `id` to the service, which fetches and caches the hero.
The getter for the `hero` property pulls the cached hero from the service. The getter for the `hero` property pulls the cached hero from the service.
And the template displays this data-bound property. And the template displays this data-bound property.
@ -239,15 +238,15 @@ a(id="optional")
a(id="qualify-dependency-lookup") a(id="qualify-dependency-lookup")
.l-main-section .l-main-section
:marked :marked
## Qualify dependency lookup with *@Optional* and *@Host* ## Qualify dependency lookup with _@Optional()_ and `@Host()`
We learned that dependencies can be registered at any level in the component hierarchy. As you now know, dependencies can be registered at any level in the component hierarchy.
When a component requests a dependency, Angular starts with that component's injector and walks up the injector tree When a component requests a dependency, Angular starts with that component's injector and walks up the injector tree
until it finds the first suitable provider. Angular throws an error if it can't find the dependency during that walk. until it finds the first suitable provider. Angular throws an error if it can't find the dependency during that walk.
We *want* this behavior most of the time. You *want* this behavior most of the time.
But sometimes we need to limit the search and/or accommodate a missing dependency. But sometimes you need to limit the search and/or accommodate a missing dependency.
We can modify Angular's search behavior with the `@Host` and `@Optional` qualifying decorators, You can modify Angular's search behavior with the `@Host` and `@Optional` qualifying decorators,
used individually or together. used individually or together.
The `@Optional` decorator tells Angular to continue when it can't find the dependency. The `@Optional` decorator tells Angular to continue when it can't find the dependency.
@ -257,17 +256,19 @@ a(id="qualify-dependency-lookup")
The host component is typically the component requesting the dependency. The host component is typically the component requesting the dependency.
But when this component is projected into a *parent* component, that parent component becomes the host. But when this component is projected into a *parent* component, that parent component becomes the host.
We look at this second, more interesting case in our next example. The next example covers this second case.
a#demonstration
:marked
### Demonstration ### Demonstration
The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](#hero-bios-component). The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that you looked at [above](#hero-bios-component).
+makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','hero-bios-and-contacts','src/app/hero-bios.component.ts (HeroBiosAndContactsComponent)') +makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','hero-bios-and-contacts','src/app/hero-bios.component.ts (HeroBiosAndContactsComponent)')
:marked :marked
Focus on the template: Focus on the template:
+makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','template')(format='.') +makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','template')(format='.')
:marked :marked
We've inserted a `<hero-contact>` element between the `<hero-bio>` tags. Now there is a new `<hero-contact>` element between the `<hero-bio>` tags.
Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view, Angular *projects*, or *transcludes*, the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
placing it in the `<ng-content>` slot of the `HeroBioComponent` template: placing it in the `<ng-content>` slot of the `HeroBioComponent` template:
+makeExample('cb-dependency-injection/ts/src/app/hero-bio.component.ts','template','src/app/hero-bio.component.ts (template)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/hero-bio.component.ts','template','src/app/hero-bio.component.ts (template)')(format='.')
:marked :marked
@ -275,58 +276,56 @@ a(id="qualify-dependency-lookup")
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact") img(src="/resources/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact")
:marked :marked
Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section: Here's the `HeroContactComponent` which demonstrates the qualifying decorators:
+makeExample('cb-dependency-injection/ts/src/app/hero-contact.component.ts','component','src/app/hero-contact.component.ts') +makeExample('cb-dependency-injection/ts/src/app/hero-contact.component.ts','component','src/app/hero-contact.component.ts')
:marked :marked
Focus on the constructor parameters Focus on the constructor parameters:
+makeExample('cb-dependency-injection/ts/src/app/hero-contact.component.ts','ctor-params','src/app/hero-contact.component.ts')(format='.') +makeExample('cb-dependency-injection/ts/src/app/hero-contact.component.ts','ctor-params','src/app/hero-contact.component.ts')(format='.')
:marked :marked
The `@Host()` function decorating the `heroCache` property ensures that The `@Host()` function decorating the `heroCache` property ensures that
we get a reference to the cache service from the parent `HeroBioComponent`. you get a reference to the cache service from the parent `HeroBioComponent`.
Angular throws if the parent lacks that service, even if a component higher in the component tree happens to have that service. Angular throws an error if the parent lacks that service, even if a component higher
in the component tree happens to have it.
A second `@Host()` function decorates the `loggerService` property. A second `@Host()` function decorates the `loggerService` property.
We know the only `LoggerService` instance in the app is provided at the `AppComponent` level. The only `LoggerService` instance in the app is provided at the `AppComponent` level.
The host `HeroBioComponent` doesn't have its own `LoggerService` provider. The host `HeroBioComponent` doesn't have its own `LoggerService` provider.
Angular would throw an error if we hadn't also decorated the property with the `@Optional()` function. Angular would throw an error if you hadn't also decorated the property with the `@Optional()` function.
Thanks to `@Optional()`, Angular sets the `loggerService` to null and the rest of the component adapts. Thanks to `@Optional()`, Angular sets the `loggerService` to null and the rest of the component adapts.
.l-sub-section
:marked
We'll come back to the `elementRef` property shortly.
:marked :marked
Here's the `HeroBiosAndContactsComponent` in action. Here's the `HeroBiosAndContactsComponent` in action.
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into") img(src="/resources/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into")
:marked :marked
If we comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree If you comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree
until it finds the logger at the `AppComponent` level. The logger logic kicks in and the hero display updates until it finds the logger at the `AppComponent` level. The logger logic kicks in and the hero display updates
with the gratuitous "!!!", indicating that the logger was found. with the gratuitous "!!!", indicating that the logger was found.
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host") img(src="/resources/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host")
:marked :marked
On the other hand, if we restore the `@Host()` decorator and comment out `@Optional`, On the other hand, if you restore the `@Host()` decorator and comment out `@Optional`,
the application fails for lack of the required logger at the host component level. the application fails for lack of the required logger at the host component level.
<br> <br>
`EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)` `EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)`
<a id="component-element"></a> <a id="component-element"></a>
:marked :marked
## Inject the component's element ## Inject the component's DOM element
On occasion we might need to access a component's corresponding DOM element. On occasion you might need to access a component's corresponding DOM element.
Although we strive to avoid it, many visual effects and 3rd party tools (such as jQuery) Although developers strive to avoid it, many visual effects and 3rd party tools, such as jQuery,
require DOM access. require DOM access.
To illustrate, we've written a simplified version of the `HighlightDirective` from To illustrate, here's a simplified version of the `HighlightDirective` from
the [Attribute Directives](../guide/attribute-directives.html) chapter. the [Attribute Directives](../guide/attribute-directives.html) page.
+makeExample('cb-dependency-injection/ts/src/app/highlight.directive.ts','','src/app/highlight.directive.ts') +makeExample('cb-dependency-injection/ts/src/app/highlight.directive.ts','','src/app/highlight.directive.ts')
:marked :marked
The directive sets the background to a highlight color when the user mouses over the The directive sets the background to a highlight color when the user mouses over the
DOM element to which it is applied. DOM element to which it is applied.
Angular set the constructor's `el` parameter to the injected `ElementRef` which is Angular sets the constructor's `el` parameter to the injected `ElementRef`, which is
a wrapper around that DOM element. a wrapper around that DOM element.
Its `nativeElement` property exposes the DOM element for the directive to manipulate. Its `nativeElement` property exposes the DOM element for the directive to manipulate.
@ -343,12 +342,11 @@ figure.image-display
:marked :marked
## Define dependencies with providers ## Define dependencies with providers
In this section we learn to write providers that deliver dependent services. This section demonstrates how to write providers that deliver dependent services.
### Background Get a service from a dependency injector by giving it a ***token***.
We get a service from a dependency injector by giving it a ***token***.
We usually let Angular handle this transaction for us by specifying a constructor parameter and its type. You usually let Angular handle this transaction by specifying a constructor parameter and its type.
The parameter type serves as the injector lookup *token*. The parameter type serves as the injector lookup *token*.
Angular passes this token to the injector and assigns the result to the parameter. Angular passes this token to the injector and assigns the result to the parameter.
Here's a typical example: Here's a typical example:
@ -366,32 +364,32 @@ figure.image-display
:marked :marked
If the injector doesn't have a provider for the requested *token*, it delegates the request If the injector doesn't have a provider for the requested *token*, it delegates the request
to its parent injector, where the process repeats until there are no more injectors. to its parent injector, where the process repeats until there are no more injectors.
If the search is futile, the injector throws an error ... unless the request was [optional](#optional). If the search is futile, the injector throws an error&mdash;unless the request was [optional](#optional).
Let's return our attention to providers themselves.
:marked :marked
A new injector has no providers. A new injector has no providers.
Angular initializes the injectors it creates with some providers it cares about. Angular initializes the injectors it creates with some providers it cares about.
We have to register our _own_ application providers manually, You have to register your _own_ application providers manually,
usually in the `providers` array of the `Component` or `Directive` metadata: usually in the `providers` array of the `Component` or `Directive` metadata:
+makeExample('cb-dependency-injection/ts/src/app/app.component.ts','providers','src/app/app.component.ts (providers)') +makeExample('cb-dependency-injection/ts/src/app/app.component.ts','providers','src/app/app.component.ts (providers)')
a#defining-providers
:marked :marked
### Defining providers ### Defining providers
The simple class provider is the most typical by far. The simple class provider is the most typical by far.
We mention the class in the `providers` array and we're done. You mention the class in the `providers` array and you're done.
+makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','class-provider','src/app/hero-bios.component.ts (class provider)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','class-provider','src/app/hero-bios.component.ts (class provider)')(format='.')
:marked :marked
It's that simple because the most common injected service is an instance of a class. It's that simple because the most common injected service is an instance of a class.
But not every dependency can be satisfied by creating a new instance of a class. But not every dependency can be satisfied by creating a new instance of a class.
We need other ways to deliver dependency values and that means we need other ways to specify a provider. 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 we need them. The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them.
figure.image-display figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px") img(src="/resources/images/cookbooks/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px")
:marked :marked
It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about. It's visually simple: a few properties and the output of a logger. 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') +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 .l-main-section
@ -402,18 +400,18 @@ a(id='provide')
The `provide` object literal takes a *token* and a *definition object*. The `provide` object literal takes a *token* and a *definition object*.
The *token* is usually a class but [it doesn't have to be](#tokens). The *token* is usually a class but [it doesn't have to be](#tokens).
The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider The *definition* object has one main property, `useValue`, that indicates how the provider
should create or return the provided value. should create or return the provided value.
.l-main-section .l-main-section
a(id='usevalue') a(id='usevalue')
:marked :marked
#### useValue - the *value provider* #### useValue&mdash;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 dependency object.
Use this technique to provide *runtime configuration constants* such as web-site base addresses and feature flags. Use this technique to provide *runtime configuration constants* such as website base addresses and feature flags.
We often use a *value provider* in a unit test to replace a production service with a fake or mock. You can use a *value provider* in a unit test to replace a production service with a fake or mock.
The `HeroOfTheMonthComponent` example has two *value providers*. The `HeroOfTheMonthComponent` example has two *value providers*.
The first provides an instance of the `Hero` class; The first provides an instance of the `Hero` class;
@ -425,9 +423,9 @@ a(id='usevalue')
The `TITLE` provider token is *not a class*. The `TITLE` provider token is *not a class*.
It's a special kind of provider lookup key called an [OpaqueToken](#opaquetoken). It's a special kind of provider lookup key called an [OpaqueToken](#opaquetoken).
We often use an `OpaqueToken` when the dependency is a simple value like a string, a number, or a function. You can use an `OpaqueToken` 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*. We can't create the value later. 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. Obviously the title string literal is immediately available.
The `someHero` variable in this example was set earlier in the file: The `someHero` variable in this example was set earlier in the file:
+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','some-hero') +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','some-hero')
@ -437,7 +435,7 @@ a(id='usevalue')
.l-main-section .l-main-section
a(id='useclass') a(id='useclass')
:marked :marked
#### useClass - the *class provider* #### useClass&mdash;the *class provider*
The `useClass` provider creates and returns new instance of the specified class. The `useClass` provider creates and returns new instance of the specified class.
@ -445,12 +443,12 @@ a(id='useclass')
The alternative could implement a different strategy, extend the default class, The alternative could implement a different strategy, extend the default class,
or fake the behavior of the real class in a test case. or fake the behavior of the real class in a test case.
We see two examples in the `HeroOfTheMonthComponent`: Here are two examples in the `HeroOfTheMonthComponent`:
+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-class')(format='.') +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-class')(format='.')
:marked :marked
The first provider is the *de-sugared*, expanded form of the most typical case in which the 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 injection token.
We wrote it in this long form to de-mystify the preferred short form. It's in this long form to de-mystify the preferred short form.
The second provider substitutes the `DateLoggerService` for the `LoggerService`. The second provider substitutes the `DateLoggerService` for the `LoggerService`.
The `LoggerService` is already registered at the `AppComponent` level. The `LoggerService` is already registered at the `AppComponent` level.
@ -466,17 +464,17 @@ a(id='useclass')
.l-main-section .l-main-section
a(id='useexisting') a(id='useexisting')
:marked :marked
#### useExisting - the *alias provider* #### _useExisting_&mdash;the *alias provider*
The `useExisting` provider maps one token to another. The `useExisting` provider maps one token to another.
In effect, the first token is an ***alias*** for the service associated with second token, In effect, the first token is an ***alias*** for the service associated with the second token,
creating ***two ways to access the same service object***. creating ***two ways to access the same service object***.
+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-existing') +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-existing')
:marked :marked
Narrowing an API through an aliasing interface is _one_ important use case for this technique. Narrowing an API through an aliasing interface is _one_ important use case for this technique.
We're aliasing for that very purpose here. 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). Imagine that the `LoggerService` had a large API; it's actually only three methods and a property.
We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](#class-interface): You'd want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](#class-interface):
+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/date-logger.service.ts','minimal-logger','src/app/date-logger.service.ts (MinimalLogger)')(format='.')
:marked :marked
@ -485,7 +483,8 @@ figure.image-display
img(src="/resources/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API") img(src="/resources/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API")
:marked :marked
Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService` Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService`
which happens to be the `DateLoggerService` thanks to the override provider registered previously via `useClass`. 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: 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") img(src="/resources/images/cookbooks/dependency-injection/date-logger-entry.png" alt="DateLoggerService entry" width="300px")
@ -493,10 +492,10 @@ figure.image-display
.l-main-section .l-main-section
a(id='usefactory') a(id='usefactory')
:marked :marked
#### useFactory - the *factory provider* #### _useFactory_&mdash;the *factory provider*
The `useFactory` provider creates a dependency object by calling a factory function The `useFactory` provider creates a dependency object by calling a factory function
as seen in this example. as in this example.
+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-factory') +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-factory')
:marked :marked
Use this technique to ***create a dependency object*** Use this technique to ***create a dependency object***
@ -507,7 +506,7 @@ a(id='usefactory')
to the "Hero of the Month" contest. to the "Hero of the Month" contest.
The local state is the number `2`, the number of runners-up this component should show. The local state is the number `2`, the number of runners-up this component should show.
We execute `runnersUpFactory` immediately with `2`. It executes `runnersUpFactory` immediately with `2`.
The `runnersUpFactory` itself isn't the provider factory function. The `runnersUpFactory` itself isn't the provider factory function.
The true provider factory function is the function that `runnersUpFactory` returns. The true provider factory function is the function that `runnersUpFactory` returns.
@ -537,34 +536,35 @@ a(id="tokens")
## Provider token alternatives: the *class-interface* and *OpaqueToken* ## Provider token alternatives: the *class-interface* and *OpaqueToken*
Angular dependency injection is easiest when the provider *token* is a class Angular dependency injection is easiest when the provider *token* is a class
that is also the type of the returned dependency object (what we usually call the *service*). that is also the type of the returned dependency object, or what you usually call the *service*.
But the token doesn't have to be a class and even when it is a class, But the token doesn't have to be a class and even when it is a class,
it doesn't have to be the same type as the returned object. it doesn't have to be the same type as the returned object.
That's the subject of our next section. That's the subject of the next section.
<a id="class-interface"></a> <a id="class-interface"></a>
:marked
### class-interface ### class-interface
In the previous *Hero of the Month* example, we used the `MinimalLogger` class The previous *Hero of the Month* example used the `MinimalLogger` class
as the token for a provider of a `LoggerService`. as the token for a provider of a `LoggerService`.
+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-existing') +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-existing')
:marked :marked
The `MinimalLogger` is an abstract class. 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/date-logger.service.ts','minimal-logger')(format='.')
:marked :marked
We usually inherit from an abstract class. You usually inherit from an abstract class.
But `LoggerService` doesn't inherit from `MinimalLogger`. *No class* inherits from it. But `LoggerService` doesn't inherit from `MinimalLogger`. *No class* inherits from it.
Instead, we use it like an interface. Instead, you use it like an interface.
Look again at the declaration for `DateLoggerService` Look again at the declaration for `DateLoggerService`:
+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','date-logger-service-signature')(format='.') +makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','date-logger-service-signature')(format='.')
:marked :marked
`DateLoggerService` inherits (extends) from `LoggerService`, not `MinimalLogger`. `DateLoggerService` inherits (extends) from `LoggerService`, not `MinimalLogger`.
The `DateLoggerService` *implements* `MinimalLogger` as if `MinimalLogger` were an *interface*. The `DateLoggerService` *implements* `MinimalLogger` as if `MinimalLogger` were an *interface*.
We call a class used in this way a ***class-interface***. When you use a class this way, it's called a ***class-interface***.
The key benefit of a *class-interface* is that we can get the strong-typing of an interface The key benefit of a *class-interface* is that you can get the strong-typing of an interface
and we can ***use it as a provider token*** in the same manner as a normal class. and you can ***use it as a provider token*** in the same manner as a normal class.
A ***class-interface*** should define *only* the members that its consumers are allowed to call. 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. Such a narrowing interface helps decouple the concrete class from its consumers.
@ -573,27 +573,27 @@ a(id="tokens")
.l-sub-section .l-sub-section
:marked :marked
#### Why *MinimalLogger* is a class and not an interface #### Why *MinimalLogger* is a class and not an interface
We can't use an interface as a provider token because You can't use an interface as a provider token because
interfaces are not JavaScript objects. interfaces are not JavaScript objects.
They exist only in the TypeScript design space. They exist only in the TypeScript design space.
They disappear after the code is transpiled to JavaScript. They disappear after the code is transpiled to JavaScript.
A provider token must be a real JavaScript object of some kind: A provider token must be a real JavaScript object of some kind:
a function, an object, a string ... a class. such as a function, an object, a string, or a class.
Using a class as an interface gives us the characteristics of an interface in a JavaScript object. Using a class as an interface gives you the characteristics of an interface in a JavaScript object.
The minimize memory cost, the class should have *no implementation*. To minimize memory cost, the class should have *no implementation*.
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript: The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript:
+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger-transpiled')(format='.') +makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger-transpiled')(format='.')
:marked :marked
It never grows larger no matter how many members we add *as long as they are typed but not implemented*. It never grows larger no matter how many members you add *as long as they are typed but not implemented*.
a(id='opaque-token') a(id='opaque-token')
:marked :marked
### OpaqueToken ### _OpaqueToken_
Dependency objects can be simple values like dates, numbers and strings or Dependency objects can be simple values like dates, numbers and strings, or
shapeless objects like arrays and functions. shapeless objects like arrays and functions.
Such objects don't have application interfaces and therefore aren't well represented by a class. Such objects don't have application interfaces and therefore aren't well represented by a class.
@ -602,11 +602,11 @@ a(id='opaque-token')
another token that happens to have the same name. another token that happens to have the same name.
The `OpaqueToken` has these characteristics. The `OpaqueToken` has these characteristics.
We encountered them twice in the *Hero of the Month* example, You encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider. 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-opaque-token')(format='.')
:marked :marked
We created the `TITLE` token like this: 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','opaque-token')(format='.')
@ -614,9 +614,9 @@ a(id="di-inheritance")
.l-main-section .l-main-section
:marked :marked
## Inject into a derived class ## Inject into a derived class
We must take care when writing a component that inherits from another component. Take care when writing a component that inherits from another component.
If the base component has injected dependencies, If the base component has injected dependencies,
we must re-provide and re-inject them in the derived class you must re-provide and re-inject them in the derived class
and then pass them down to the base class through the constructor. and then pass them down to the base class through the constructor.
In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent` In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent`
@ -632,30 +632,28 @@ figure.image-display
+makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','heroes-base','src/app/sorted-heroes.component.ts (HeroesBaseComponent)') +makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','heroes-base','src/app/sorted-heroes.component.ts (HeroesBaseComponent)')
.l-sub-section .l-sub-section
:marked :marked
We strongly prefer simple constructors. They should do little more than initialize variables. ***Keep constructors simple.*** They should do little more than initialize variables.
This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server. This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server.
That's why we call the `HeroService` from within the `ngOnInit` rather than the constructor. That's why you call the `HeroService` from within the `ngOnInit` rather than the constructor.
We explain the mysterious `afterGetHeroes` below.
:marked :marked
Users want to see the heroes in alphabetical order. Users want to see the heroes in alphabetical order.
Rather than modify the original component, we sub-class it and create a Rather than modify the original component, sub-class it and create a
`SortedHeroesComponent` that sorts the heroes before presenting them. `SortedHeroesComponent` that sorts the heroes before presenting them.
The `SortedHeroesComponent` lets the base class fetch the heroes. The `SortedHeroesComponent` lets the base class fetch the heroes.
(we said it was contrived).
Unfortunately, Angular cannot inject the `HeroService` directly into the base class. Unfortunately, Angular cannot inject the `HeroService` directly into the base class.
We must provide the `HeroService` again for *this* component, You must provide the `HeroService` again for *this* component,
then pass it down to the base class inside the constructor. then pass it down to the base class inside the constructor.
+makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','sorted-heroes','src/app/sorted-heroes.component.ts (SortedHeroesComponent)') +makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','sorted-heroes','src/app/sorted-heroes.component.ts (SortedHeroesComponent)')
:marked :marked
Now take note of the `afterGetHeroes` method. Now take note of the `afterGetHeroes()` method.
Our first instinct was to create an `ngOnInit` method in `SortedHeroesComponent` and do the sorting there. Your first instinct might have been to create an `ngOnInit` method in `SortedHeroesComponent` and do the sorting there.
But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit` But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit`
so we'd be sorting the heroes array *before they arrived*. That produces a nasty error. so you'd be sorting the heroes array *before they arrived*. That produces a nasty error.
Overriding the base class's `afterGetHeroes` method solves the problem Overriding the base class's `afterGetHeroes()` method solves the problem.
These complications argue for *avoiding component inheritance*. These complications argue for *avoiding component inheritance*.
@ -665,27 +663,29 @@ a(id="find-parent")
## Find a parent component by injection ## Find a parent component by injection
Application components often need to share information. Application components often need to share information.
We prefer the more loosely coupled techniques such as data binding and service sharing. More loosely coupled techniques such as data binding and service sharing
But sometimes it makes sense for one component to have a direct reference to another component are preferable. But sometimes it makes sense for one component
to have a direct reference to another component
perhaps to access values or call methods on that component. perhaps to access values or call methods on that component.
Obtaining a component reference is a bit tricky in Angular. Obtaining a component reference is a bit tricky in Angular.
Although an Angular application is a tree of components, Although an Angular application is a tree of components,
there is no public API for inspecting and traversing that tree. there is no public API for inspecting and traversing that tree.
There is an API for acquiring a child reference There is an API for acquiring a child reference.
(checkout `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`). Check out `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`
in the [API Reference](../api/).
There is no public API for acquiring a parent reference. There is no public API for acquiring a parent reference.
But because every component instance is added to an injector's container, But because every component instance is added to an injector's container,
we can use Angular dependency injection to reach a parent component. you can use Angular dependency injection to reach a parent component.
This section describes some techniques for doing that. This section describes some techniques for doing that.
<a id="known-parent"></a> <a id="known-parent"></a>
### Find a parent component of known type ### Find a parent component of known type
We use standard class injection to acquire a parent component whose type we know. You use standard class injection to acquire a parent component whose type you know.
In the following example, the parent `AlexComponent` has several children including a `CathyComponent`: In the following example, the parent `AlexComponent` has several children including a `CathyComponent`:
a(id='alex') a(id='alex')
@ -695,35 +695,38 @@ a(id='alex')
after injecting an `AlexComponent` into her constructor: after injecting an `AlexComponent` into her constructor:
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','cathy','parent-finder.component.ts (CathyComponent)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','cathy','parent-finder.component.ts (CathyComponent)')(format='.')
:marked :marked
We added the [@Optional](#optional) qualifier for safety but Notice that even though the [@Optional](#optional) qualifier
is there for safety,
the <live-example name="cb-dependency-injection"></live-example> the <live-example name="cb-dependency-injection"></live-example>
confirms that the `alex` parameter is set. confirms that the `alex` parameter is set.
<a id="base-parent"></a> <a id="base-parent"></a>
### Cannot find a parent by its base class ### Cannot find a parent by its base class
What if we do *not* know the concrete parent component class? What if you *don't* know the concrete parent component class?
A re-usable component might be a child of multiple components. A re-usable component might be a child of multiple components.
Imagine a component for rendering breaking news about a financial instrument. Imagine a component for rendering breaking news about a financial instrument.
For sound (cough) business reasons, this news component makes frequent calls For business reasons, this news component makes frequent calls
directly into its parent instrument as changing market data stream by. directly into its parent instrument as changing market data streams by.
The app probably defines more than a dozen financial instrument components. The app probably defines more than a dozen financial instrument components.
If we're lucky, they all implement the same base class If you're lucky, they all implement the same base class
whose API our `NewsComponent` understands. whose API your `NewsComponent` understands.
.l-sub-section .l-sub-section
:marked :marked
Looking for components that implement an interface would be better. Looking for components that implement an interface would be better.
That's not possible because TypeScript interfaces disappear from the transpiled JavaScript That's not possible because TypeScript interfaces disappear
which doesn't support interfaces. There's no artifact we could look for. from the transpiled JavaScript, which doesn't support interfaces.
There's no artifact to look for.
:marked :marked
We're not claiming this is good design. This isn't necessarily good design.
We are asking *can a component inject its parent via the parent's base class*? This example is examining *whether a component can
inject its parent via the parent's base class*.
The sample's `CraigComponent` explores this question. [Looking back](#alex) The sample's `CraigComponent` explores this question. [Looking back](#alex),
we see that the `Alex` component *extends* (*inherits*) from a class named `Base`. you see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (Alex class signature)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (Alex class signature)')(format='.')
:marked :marked
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded. The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
@ -732,28 +735,29 @@ a(id='alex')
Unfortunately, this does not work. Unfortunately, this does not work.
The <live-example name="cb-dependency-injection"></live-example> The <live-example name="cb-dependency-injection"></live-example>
confirms that the `alex` parameter is null. confirms that the `alex` parameter is null.
*We cannot inject a parent by its base class.* *You cannot inject a parent by its base class.*
<a id="class-interface-parent"></a> <a id="class-interface-parent"></a>
### Find a parent by its class-interface ### Find a parent by its class-interface
We can find a parent component with a [class-interface](#class-interface). You can find a parent component with a [class-interface](#class-interface).
The parent must cooperate by providing an *alias* to itself in the name of a *class-interface* token. The parent must cooperate by providing an *alias* to itself in the name of a *class-interface* token.
Recall that Angular always adds a component instance to its own injector; Recall that Angular always adds a component instance to its own injector;
that's why we could inject *Alex* into *Cathy* [earlier](#known-parent). that's why you could inject *Alex* into *Cathy* [earlier](#known-parent).
We write an [*alias provider*](#useexisting) &mdash; a `provide` object literal with a `useExisting` definition &mdash; Write an [*alias provider*](#useexisting)&mdash;a `provide` object literal with a `useExisting`
that creates an *alternative* way to inject the same component instance definition&mdash;that creates an *alternative* way to inject the same component instance
and add that provider to the `providers` array of the `@Component` metadata for the `AlexComponent`: and add that provider to the `providers` array of the `@Component` metadata for the `AlexComponent`:
a(id="alex-providers") a(id="alex-providers")
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.')
:marked :marked
[Parent](#parent-token) is the provider's *class-interface* token. [Parent](#parent-token) is the provider's *class-interface* token.
The [*forwardRef*](#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself. The [*forwardRef*](#forwardref) breaks the circular reference you just created by having the `AlexComponent` refer to itself.
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before: *Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter,
the same way you've done it before:
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','carol-class','parent-finder.component.ts (CarolComponent class)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','carol-class','parent-finder.component.ts (CarolComponent class)')(format='.')
:marked :marked
Here's *Alex* and family in action: Here's *Alex* and family in action:
@ -762,7 +766,7 @@ figure.image-display
a(id="parent-tree") a(id="parent-tree")
:marked :marked
### Find the parent in a tree of parents ### Find the parent in a tree of parents with _@SkipSelf()_
Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*. Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*.
Both *Alice* and *Barry* implement the `Parent` *class-interface*. Both *Alice* and *Barry* implement the `Parent` *class-interface*.
@ -775,7 +779,7 @@ a(id="parent-tree")
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','barry','parent-finder.component.ts (BarryComponent)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','barry','parent-finder.component.ts (BarryComponent)')(format='.')
:marked :marked
*Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers). *Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers).
If we're going to keep writing [*alias providers*](#useexisting) like this we should create a [helper function](#provideparent). If you're going to keep writing [*alias providers*](#useexisting) like this you should create a [helper function](#provideparent).
For now, focus on *Barry*'s constructor: For now, focus on *Barry*'s constructor:
+makeTabs( +makeTabs(
@ -791,7 +795,7 @@ a(id="parent-tree")
1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself, 1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself,
which *is* what parent means. which *is* what parent means.
2. Angular throws a cyclic dependency error if we omit the `@SkipSelf` decorator. 2. Angular throws a cyclic dependency error if you omit the `@SkipSelf` decorator.
`Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)` `Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)`
@ -803,20 +807,20 @@ figure.image-display
a(id="parent-token") a(id="parent-token")
:marked :marked
### The *Parent* class-interface ### The *Parent* class-interface
We [learned earlier](#class-interface) that a *class-interface* is an abstract class used as an interface rather than as a base class. You [learned earlier](#class-interface) that a *class-interface* is an abstract class used as an interface rather than as a base class.
Our example defines a `Parent` *class-interface* . The example defines a `Parent` *class-interface*.
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','parent','parent-finder.component.ts (Parent class-interface)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','parent','parent-finder.component.ts (Parent class-interface)')(format='.')
:marked :marked
The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*., The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*.
The `name` property is the only member of a parent component that a child component can call. The `name` property is the only member of a parent component that a child component can call.
Such a narrowing interface helps decouple the child component class from its parent components. Such a narrow interface helps decouple the child component class from its parent components.
A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does: A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does:
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alice-class-signature','parent-finder.component.ts (AliceComponent class signature)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alice-class-signature','parent-finder.component.ts (AliceComponent class signature)')(format='.')
:marked :marked
Doing so adds clarity to the code. But it's not technically necessary. Doing so adds clarity to the code. But it's not technically necessary.
Although the `AlexComponent` has a `name` property (as required by its `Base` class) Although the `AlexComponent` has a `name` property, as required by its `Base` class,
its class signature doesn't mention `Parent`: its class signature doesn't mention `Parent`:
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (AlexComponent class signature)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (AlexComponent class signature)')(format='.')
.l-sub-section .l-sub-section
@ -826,25 +830,25 @@ a(id="parent-token")
a(id="provideparent") a(id="provideparent")
:marked :marked
### A *provideParent* helper function ### A _provideParent()_ helper function
Writing variations of the same parent *alias provider* gets old quickly, Writing variations of the same parent *alias provider* gets old quickly,
especially this awful mouthful with a [*forwardRef*](#forwardref): especially this awful mouthful with a [*forwardRef*](#forwardref):
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers')(format='.')
:marked :marked
We can extract that logic into a helper function like this: You can extract that logic into a helper function like this:
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','provide-the-parent')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','provide-the-parent')(format='.')
:marked :marked
Now we can add a simpler, more meaningful parent provider to our components: Now you can add a simpler, more meaningful parent provider to your components:
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alice-providers')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alice-providers')(format='.')
:marked :marked
We can do better. The current version of the helper function can only alias the `Parent` *class-interface*. You can do better. The current version of the helper function can only alias the `Parent` *class-interface*.
Our application might have a variety of parent types, each with its own *class-interface* token. The application might have a variety of parent types, each with its own *class-interface* token.
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*. Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*.
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','provide-parent')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','provide-parent')(format='.')
:marked :marked
And here's how we could use it with a different parent type: And here's how you could use it with a different parent type:
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','beth-providers')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','beth-providers')(format='.')
a(id="forwardref") a(id="forwardref")
@ -853,23 +857,23 @@ a(id="forwardref")
## Break circularities with a forward class reference (*forwardRef*) ## Break circularities with a forward class reference (*forwardRef*)
The order of class declaration matters in TypeScript. The order of class declaration matters in TypeScript.
We can't refer directly to a class until it's been defined. You can't refer directly to a class until it's been defined.
This isn't usually a problem, especially if we adhere to the recommended *one class per file* rule. This isn't usually a problem, especially if you adhere to the recommended *one class per file* rule.
But sometimes circular references are unavoidable. But sometimes circular references are unavoidable.
We're in a bind when class 'A refers to class 'B' and 'B' refers to 'A'. You're in a bind when class 'A' refers to class 'B' and 'B' refers to 'A'.
One of them has to be defined first. One of them has to be defined first.
The Angular `forwardRef` function creates an *indirect* reference that Angular can resolve later. The Angular `forwardRef()` function creates an *indirect* reference that Angular can resolve later.
The *Parent Finder* sample is full of circular class references that are impossible to break. The *Parent Finder* sample is full of circular class references that are impossible to break.
:marked :marked
We face this dilemma when a class makes *a reference to itself* You face this dilemma when a class makes *a reference to itself*
as does the `AlexComponent` in its `providers` array. as does the `AlexComponent` in its `providers` array.
The `providers` array is a property of the `@Component` decorator function which must The `providers` array is a property of the `@Component` decorator function which must
appear *above* the class definition. appear *above* the class definition.
We break the circularity with `forwardRef`: Break the circularity with `forwardRef`:
+makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.')
:marked :marked