Skip to content

Commit

Permalink
docs: add some more
Browse files Browse the repository at this point in the history
  • Loading branch information
kapunahelewong committed Feb 21, 2019
1 parent a76e16d commit 7192e09
Showing 1 changed file with 80 additions and 30 deletions.
110 changes: 80 additions & 30 deletions aio/content/guide/hierarchical-dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,55 @@ Injectors in Angular allow you to determine where you provide services.

There are two injector hierarchies in Angular:

1. The `ModuleInjector`—injector created as a result of @NgModule
1. The `ModuleInjector`—injector created as a result of the service, or, prior to Angular v.6, @NgModule
<!-- Is #1 correct now that we recommend providedIn? -->
1. The `NodeInjector`&mdash;injector created as a result of a component/directive on an element

### `ModuleInjector`

A ModuleInjector is created in one of two ways:

* The `@Injectable()` `providedIn` property configured with an `@NgModule`, such as a lazy loaded feature module or `root`.
* From the `@NgModule` `providers` array (prior to Angular v.6). If an @NgModule imports other @NgModules, all of those providers from imported @NgModules are flattened into a single `ModuleInjector`. Create child `ModuleInjector`s by lazy loading other @NgModules, not by importing them.
1. The `@Injectable()` `providedIn` property configured with an @NgModule, such as a lazy loaded feature module or `root`.
1. From the `@NgModule` `providers` array (prior to Angular v.6). If an @NgModule imports other @NgModules, all of those providers from imported @NgModules are flattened into a single `ModuleInjector`. Create child `ModuleInjector`s by lazy loading other @NgModules, not by importing them.

`NodeInjector`
Provide services in your serevice via the `providedIn` property of `@Injectable()` as follows:

A component or directive creates an `NodeInjector` and Anguar gives it a `ModuleInjector`.
```ts

import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class ItemService {
name = 'telephone';
}

```

The above example provides the ItemService in the `root` `ModuleInjector`, but you can specify another `@NgModule`.

<div class="is-helpful alert">

Using the `@Injectable()` `providedIn` property is preferable to the `@NgModule` `providers` array because with `@Injectable()` `providedIn`, unused services are tree-shakable. However, when using the `@NgModule` `providers` array, unused services
are not tree-shakable.

</div>

### `NodeInjector`

A component or directive creates an `NodeInjector` and Angular gives it a `ModuleInjector`.
When you provide a service in the `@Component()` decorator using its `providers` property, Angular creates a `NodeInjector`. For example, if you have a `TestComponent`, you'd take advantage of (create?) the `NodeInjector` by providing your service as follows:
<!-- Is the NodeInjector created even if you don't provide a service in the component? -->

```ts
@Component({
...
providers: [{ provide: ItemService, useValue: { name: 'television' } }]
})
export class TestComponent

```

<div class="alert is-helpful">
**Note:** `ModuleInjector` is not a parent of `NodeInjector`. Each element can in theory have a different `ModuleInjector`.
Expand Down Expand Up @@ -53,27 +88,24 @@ NEED IMAGES and example. -->
## Resolution modifiers

You can modify the default resolution behavior with `@Optional()`, `@Self()`,
`@SkipSelf()` and `@Host()`. Use each in the component class constructor when you inject your service.
`@SkipSelf()` and `@Host()`. Import each of them from `@angular/core` and use each in the component class constructor when you inject your service.

### `@Self()`

Use `@Self()` so that Angular will only look at the `NodeInjector` for the current component. For example, in the following `SelfComponent`, notice the import statements, the `@Component()` `providers` array, and the constructor.

```ts
// import Self and your service
import { Component, OnInit, Self } from '@angular/core';
import { LeafService } from '../leaf.service';

@Component({
...
// specify the service and a value (
// specify the service and a value
providers: [{ provide: LeafService, useValue: { name: '🌱' } }]
})
export class SelfComponent {
constructor(@Self() public leaf : LeafService) { }
}
```
<!-- Is this precise? ^^-->
<!-- Is this precise? Is it a good example? ^^-->

In this example, Angular will stop looking for the LeafService at the
`SelfComponent` and will go no higher in the `NodeInjector` tree. In
Expand All @@ -90,19 +122,16 @@ in the current one. So if the parent component were using the value `🌿`
for `name` , but you had `🌱` in the component's `providers` array,
Angular would ignore `🌱` and use `🌿`.

Assume that the following value for `name` is what the parent component were using:
To see this in code, assume that the following value for `name` is what the parent component were using, as in this service:

```ts
export class LeafService {
name = "🌿";
name = '🌿';
}
```
Imagine that in the child component, you had a different value, but you wanted to use the parent's value instead.
Imagine that in the child component, you had a different value, but you wanted to use the parent's value instead. This is when you'd use `@SkipSelf()`:

```ts
// import SkipSelf and your service
import { Component, OnInit, SkipSelf } from '@angular/core';
import { LeafService } from '../leaf.service';

@Component({
...
Expand All @@ -115,16 +144,19 @@ export class SkipselfComponent {

}
```
<!-- ^^Is there anyway that I could make this example more applicable to the real world? -->

In this case, the value you'd get for name would be `🌿`, not `🌱`.

### `@Optional()`

`@Optional()` allows Angular to consider a service you inject to be optional. This way, if for some reason it is unavailable at runtime, Angular simply assigns a value of null to the service, rather than throwing an error. In the following example, the service, `OptionalService` isn't provided in the service, NgModule, or component class.
`@Optional()` allows Angular to consider a service you inject to be optional.
This way, if for some reason it is unavailable at runtime, Angular simply
assigns a value of null to the service, rather than throwing an error. In
the following example, the service, `OptionalService` isn't provided in
the service, NgModule, or component class, so it isn't available
anywhere in the app.

```ts
import { Component, OnInit, Optional } from '@angular/core';
import { OptionalService } from '../optional.service';

@Component({
providers: []
})
Expand All @@ -140,20 +172,38 @@ export class OptionalComponent {

### `@Host()`

You can cap the bubbling by adding the `@Host()` parameter
decorator on the dependant-service parameter
in a component's constructor.
The hunt for providers stops at the injector for the host element of the component.
`@Host()` lets you designate a component as the last stop in the injector tree when searching for providers. Even if there is a service instance further up the tree, Angular won't continue looking. Use `@Host()` as follows:

```ts

@Component({
...
// provide the service
providers: [{ provide: FlowerService, useValue: { name: '🌼' } }]
})
export class HostComponent {
// use @Host() in the constructor when injecting the service
constructor(@Host() public flower : FlowerService) { }
}

```

Since `HostComponent` has `@Host()` in its constructor, no matter what the parent of `HostComponent` might have as a `flower.name` value,
the `HostComponent` will use `🌼`. The children of `HostComponent`, so long as they don't provide their own value for `flower.name` will look up the tree to get the value and stop as soon as they encounter an `@Host()`. To see an example of this, in action, see the host components in the <live-example name="di-resolution-modifiers"></live-exmaple>.

* See an [example](guide/dependency-injection-in-action#qualify-dependency-lookup) of using `@Host` together with `@Optional`, another parameter decorator that lets you handle the null case if no provider is found.

<!-- The following will be changed to a regular <live-example>. Putting my stackblitz link that I used to figure out what each did in here temporarily in the interest of time. -->
To see an example of each of the resolution modifiers in a working app, see [My Temporary Demo](https://stackblitz.com/edit/injector-modifiers).


* See an [example](guide/dependency-injection-in-action#qualify-dependency-lookup) of using `@Host` together with `@Optional`.

<!--
Questions about resolution modifiers:
`@Optional()`: Optional in either injector tree
`@Self()`: Only looks to own....NodeInjector? it never looks to the ModuleInjector?
`@SkipSelf()`: starts looking from the parent NodeInjector, and if not found, continues to ModuleInjector from parent?
`@Host()`: Only looks to own NodeInjector? -->


Every resolution has a

For Module Injector, Tree-shakable injectors are preferable

Expand Down

0 comments on commit 7192e09

Please sign in to comment.