Skip to content

Commit

Permalink
docs: edit copy and example in hierarchical injectors guide (#32501)
Browse files Browse the repository at this point in the history
Fixes #32461

PR Close #32501
  • Loading branch information
kapunahelewong authored and matsko committed Sep 6, 2019
1 parent 1716b91 commit 33038f6
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,19 @@ export class AppComponent {
constructor(public flower: FlowerService, public animal: AnimalService) {}
}
// #enddocregion inject-animal-service

// When using @Host() together with @SkipSelf() in
// child.component.ts for the AnimalService, add the
// following viewProviders array to the @Component metadata:

// viewProviders: [{ provide: AnimalService, useValue: { emoji: 'πŸ¦”' } }]

// So, the entire @ChildComponent() decorator and its
// metadata should be as follows:

// @Component({
// selector: 'app-root',
// templateUrl: './app.component.html',
// styleUrls: [ './app.component.css' ],
// viewProviders: [{ provide: AnimalService, useValue: { emoji: 'πŸ¦”' } }]
// })
69 changes: 37 additions & 32 deletions aio/content/guide/hierarchical-dependency-injection.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Hierarchical injectors

Injectors in Angular have rules that you can leverage to
achieve the desired visibility in your apps.
achieve the desired visibility of injectables in your apps.
By understanding these rules, you can determine in which
provider you should declare a provider.
NgModule or component you should declare a provider.

## Two injector hierarchies

Expand Down Expand Up @@ -146,14 +146,6 @@ in the `providers` list of the `AppModule`.

Angular creates `ElementInjector`s implicitly for each DOM element.

<div class="alert is-helpful">

**Note:** Specifically, `ElementInjector` is more nunaced
in that they are created _sparsely_. For a mental model
though, assume that each DOM element gets an `ElementInjector`.

</div>

Providing a service in the `@Component()` decorator using
its `providers` or `viewProviders`
property configures an `ElementInjector`.
Expand All @@ -171,10 +163,10 @@ export class TestComponent

<div class="alert is-helpful">

**Note:** `ModuleInjector` is not a parent of `ElementInjector`.
In theory, each element can have a different `ModuleInjector`.
Think of it as `ModuleInjector` is plan-b when the
`ElementInjector` hierarchy can't resolve it.
**Note:** Please see the
[resolution rules](guide/hierarchical-dependency-injection#resolution-rules)
section to understand the relationship between the `ModuleInjector` tree and
the `ElementInjector` tree.

</div>

Expand Down Expand Up @@ -546,7 +538,7 @@ In the example case, the constraints are:
- The ending location just happens to be the same as the component
itself, because it is the topmost component in this application.

2. The `MyAppModule` acts as the fallback injector when the
2. The `AppModule` acts as the fallback injector when the
injection token can't be found in the `ElementInjector`s.

### Using the `providers` array
Expand All @@ -570,7 +562,7 @@ The next step is to add a binding to the `ChildComponent` template.
</code-example>

To render the new values, add `<app-child>` to the bottom of
the`MyAppComponent` template so the view also displays the sunflower:
the`AppComponent` template so the view also displays the sunflower:

```
Child Component
Expand All @@ -580,7 +572,7 @@ Emoji from FlowerService: 🌻
In the logical tree, this would be represented as follows:

```
<app-root @NgModule(MyAppModule)
<app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺">
<#VIEW>
<p>Emoji from FlowerService: {{flower.emoji}} (🌺)</p>
Expand Down Expand Up @@ -656,7 +648,7 @@ it has a value of 🐢 (puppy).

</code-example>

Add bindings to the `ChildComponent` and the `MyAppComponent` templates.
Add bindings to the `ChildComponent` and the `AppComponent` templates.
In the `ChildComponent` template, add the following binding:

<code-example path="providers-viewproviders/src/app/child/child.component.html" header="providers-viewproviders/src/app/child.component.html" region="animal-binding">
Expand Down Expand Up @@ -848,7 +840,7 @@ Emoji from FlowerService: 🌺
In a logical tree, this same idea might look like this:

```
<app-root @NgModule(MyAppModule)
<app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺">
<#VIEW>
<app-child @Provide(FlowerService="🌻")>
Expand All @@ -871,7 +863,7 @@ because `@Host()` limits the upper bound of the search to the
`<#VIEW>`. Here's the idea in the logical tree:

```
<app-root @NgModule(MyAppModule)
<app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺">
<#VIEW> <!-- end search here with null-->
<app-child @Provide(FlowerService="🌻")> <!-- start search here -->
Expand Down Expand Up @@ -902,7 +894,7 @@ for the `AnimalService`, it never sees the 🐳 (whale).

Just as in the `FlowerService` example, if you add `@SkipSelf()`
to the constructor for the `AnimalService`, the injector won't
look in the current `<app-parent>`'s `ElementInjector` for the
look in the current `<app-child>`'s `ElementInjector` for the
`AnimalService`.

```typescript=
Expand All @@ -914,7 +906,7 @@ export class ChildComponent {
}
```

Instead, the injector will begin at the `<app-child>`
Instead, the injector will begin at the `<app-root>`
`ElementInjector`. Remember that the `<app-child>` class
provides the `AnimalService` in the `viewProviders` array
with a value of 🐢 (puppy):
Expand All @@ -931,7 +923,7 @@ with a value of 🐢 (puppy):
The logical tree looks like this with `@SkipSelf()` in `<app-child>`:

```
<app-root @NgModule(MyAppModule)
<app-root @NgModule(AppModule)
@Inject(AnimalService=>"🐳")>
<#VIEW><!-- search begins here -->
<app-child>
Expand Down Expand Up @@ -985,9 +977,21 @@ export class ChildComponent {
</app-root>
```

However, if you use `@Host()` and `@SkipSelf()` for the `AnimalService`
as follows, you'll get 🐢 (puppy) because that's the value in the
`<app-child>`. Here are `@Host()` and `@SkipSelf()` in the `<app-child>`
Add a `viewProviders` array with a third animal, πŸ¦” (hedgehog), to the
`app.component.ts` `@Component()` metadata:

```typescript
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ],
viewProviders: [{ provide: AnimalService, useValue: { emoji: 'πŸ¦”' } }]
})
```

Next, add `@SkipSelf()` along with `@Host()` to the constructor for the
`Animal Service` in `child.component.ts`. Here are `@Host()`
and `@SkipSelf()` in the `<app-child>`
constructor :

```ts
Expand All @@ -1007,15 +1011,16 @@ which is in the `providers` array, the result was `null` because
`FlowerService` is visible in `<app-child>`, not its `<#VIEW>`.

However, the `AnimalService`, which is provided in the
`ParentComponent` `viewProviders` array, is visible.
`AppComponent` `viewProviders` array, is visible.

The logical tree representation shows why this is:

```html
<app-root @NgModule(MyAppModule)
<app-root @NgModule(AppModule)
@Inject(AnimalService=>"🐳")>
<#VIEW>
<!-- ^^@Host()+@SkipSelf() stop here^^ -->
<#VIEW @Provide(AnimalService="πŸ¦”")
@Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"πŸ¦”">
<!-- ^^@SkipSelf() starts here, @Host() stops here^^ -->
<app-child>
<#VIEW @Provide(AnimalService="🐢")
@Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"🐢">
Expand All @@ -1030,8 +1035,8 @@ The logical tree representation shows why this is:
the `AnimalService` at the `<app-root>`, not the `<app-child>`,
where the request originates, and `@Host()` stops the search
at the `<app-root>` `<#VIEW>`. Since `AnimalService` is
provided via the `viewProviders` array, the injector finds 🐢
(puppy) in the `<#VIEW>`.
provided via the `viewProviders` array, the injector finds πŸ¦”
(hedgehog) in the `<#VIEW>`.


{@a component-injectors}
Expand Down

0 comments on commit 33038f6

Please sign in to comment.