docs: edit copy and example in hierarchical injectors guide (#32501)

Fixes #32461

PR Close #32501
This commit is contained in:
Kapunahele Wong 2019-08-23 09:02:26 -04:00 committed by Matias Niemelä
parent 1716b91334
commit 33038f6ebe
2 changed files with 53 additions and 32 deletions

View File

@ -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: '🦔' } }]
// })

View File

@ -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
@ -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`.
@ -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>
@ -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
@ -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
@ -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>
@ -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">
@ -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="🌻")>
@ -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 -->
@ -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=
@ -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):
@ -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>
@ -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
@ -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)=>"🐶">
@ -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}