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) {} constructor(public flower: FlowerService, public animal: AnimalService) {}
} }
// #enddocregion inject-animal-service // #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 # Hierarchical injectors
Injectors in Angular have rules that you can leverage to 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 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 ## Two injector hierarchies
@ -146,14 +146,6 @@ in the `providers` list of the `AppModule`.
Angular creates `ElementInjector`s implicitly for each DOM element. 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 Providing a service in the `@Component()` decorator using
its `providers` or `viewProviders` its `providers` or `viewProviders`
property configures an `ElementInjector`. property configures an `ElementInjector`.
@ -171,10 +163,10 @@ export class TestComponent
<div class="alert is-helpful"> <div class="alert is-helpful">
**Note:** `ModuleInjector` is not a parent of `ElementInjector`. **Note:** Please see the
In theory, each element can have a different `ModuleInjector`. [resolution rules](guide/hierarchical-dependency-injection#resolution-rules)
Think of it as `ModuleInjector` is plan-b when the section to understand the relationship between the `ModuleInjector` tree and
`ElementInjector` hierarchy can't resolve it. the `ElementInjector` tree.
</div> </div>
@ -546,7 +538,7 @@ In the example case, the constraints are:
- The ending location just happens to be the same as the component - The ending location just happens to be the same as the component
itself, because it is the topmost component in this application. 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. injection token can't be found in the `ElementInjector`s.
### Using the `providers` array ### Using the `providers` array
@ -570,7 +562,7 @@ The next step is to add a binding to the `ChildComponent` template.
</code-example> </code-example>
To render the new values, add `<app-child>` to the bottom of 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 Child Component
@ -580,7 +572,7 @@ Emoji from FlowerService: 🌻
In the logical tree, this would be represented as follows: In the logical tree, this would be represented as follows:
``` ```
<app-root @NgModule(MyAppModule) <app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺"> @Inject(FlowerService) flower=>"🌺">
<#VIEW> <#VIEW>
<p>Emoji from FlowerService: {{flower.emoji}} (🌺)</p> <p>Emoji from FlowerService: {{flower.emoji}} (🌺)</p>
@ -656,7 +648,7 @@ it has a value of 🐶 (puppy).
</code-example> </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: 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"> <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: In a logical tree, this same idea might look like this:
``` ```
<app-root @NgModule(MyAppModule) <app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺"> @Inject(FlowerService) flower=>"🌺">
<#VIEW> <#VIEW>
<app-child @Provide(FlowerService="🌻")> <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: `<#VIEW>`. Here's the idea in the logical tree:
``` ```
<app-root @NgModule(MyAppModule) <app-root @NgModule(AppModule)
@Inject(FlowerService) flower=>"🌺"> @Inject(FlowerService) flower=>"🌺">
<#VIEW> <!-- end search here with null--> <#VIEW> <!-- end search here with null-->
<app-child @Provide(FlowerService="🌻")> <!-- start search here --> <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()` Just as in the `FlowerService` example, if you add `@SkipSelf()`
to the constructor for the `AnimalService`, the injector won't 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`. `AnimalService`.
```typescript= ```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 `ElementInjector`. Remember that the `<app-child>` class
provides the `AnimalService` in the `viewProviders` array provides the `AnimalService` in the `viewProviders` array
with a value of 🐶 (puppy): 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>`: The logical tree looks like this with `@SkipSelf()` in `<app-child>`:
``` ```
<app-root @NgModule(MyAppModule) <app-root @NgModule(AppModule)
@Inject(AnimalService=>"🐳")> @Inject(AnimalService=>"🐳")>
<#VIEW><!-- search begins here --> <#VIEW><!-- search begins here -->
<app-child> <app-child>
@ -985,9 +977,21 @@ export class ChildComponent {
</app-root> </app-root>
``` ```
However, if you use `@Host()` and `@SkipSelf()` for the `AnimalService` Add a `viewProviders` array with a third animal, 🦔 (hedgehog), to the
as follows, you'll get 🐶 (puppy) because that's the value in the `app.component.ts` `@Component()` metadata:
`<app-child>`. Here are `@Host()` and `@SkipSelf()` in the `<app-child>`
```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 : constructor :
```ts ```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>`. `FlowerService` is visible in `<app-child>`, not its `<#VIEW>`.
However, the `AnimalService`, which is provided in the 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: The logical tree representation shows why this is:
```html ```html
<app-root @NgModule(MyAppModule) <app-root @NgModule(AppModule)
@Inject(AnimalService=>"🐳")> @Inject(AnimalService=>"🐳")>
<#VIEW> <#VIEW @Provide(AnimalService="🦔")
<!-- ^^@Host()+@SkipSelf() stop here^^ --> @Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"🦔">
<!-- ^^@SkipSelf() starts here, @Host() stops here^^ -->
<app-child> <app-child>
<#VIEW @Provide(AnimalService="🐶") <#VIEW @Provide(AnimalService="🐶")
@Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"🐶"> @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>`, the `AnimalService` at the `<app-root>`, not the `<app-child>`,
where the request originates, and `@Host()` stops the search where the request originates, and `@Host()` stops the search
at the `<app-root>` `<#VIEW>`. Since `AnimalService` is at the `<app-root>` `<#VIEW>`. Since `AnimalService` is
provided via the `viewProviders` array, the injector finds 🐶 provided via the `viewProviders` array, the injector finds 🦔
(puppy) in the `<#VIEW>`. (hedgehog) in the `<#VIEW>`.
{@a component-injectors} {@a component-injectors}