docs: move di-in-action doc to conceptual ref section (#39544)

Removes duplicate info, moves document into conceptual
reference section, but doesn't edit remaining content.
Groups two dependency injection documents together in
one expandable nav section.

PR Close #39544
This commit is contained in:
Kapunahele Wong 2020-10-30 10:41:33 -04:00 committed by Andrew Kushnir
parent 1951342124
commit 61506404a9
2 changed files with 17 additions and 177 deletions

View File

@ -1,91 +1,15 @@
# Dependency injection in action # Dependency injection in action
This section explores many of the features of dependency injection (DI) in Angular. This guide explores many of the features of dependency injection (DI) in Angular.
{@a toc}
See the <live-example name="dependency-injection-in-action"></live-example>
of the code in this cookbook.
{@a nested-dependencies}
## Nested service dependencies
The _consumer_ of an injected service doesn't need to know how to create that service.
It's the job of the DI framework to create and cache dependencies. The consumer just
needs to let the DI framework know which dependencies it needs.
Sometimes a service depends on other services, which may depend on yet other services.
The dependency injection framework resolves these nested dependencies in the correct order.
At each step, the consumer of dependencies declares what it requires in its
constructor, and lets the framework provide them.
The following example shows that `AppComponent` declares its dependence on `LoggerService` and `UserContext`.
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" header="src/app/app.component.ts"></code-example>
`UserContext` in turn depends on both `LoggerService` and
`UserService`, another service that gathers information about a particular user.
<code-example path="dependency-injection-in-action/src/app/user-context.service.ts" region="injectables" header="user-context.service.ts (injection)"></code-example>
When Angular creates `AppComponent`, the DI framework creates an instance of `LoggerService` and starts to create `UserContextService`.
`UserContextService` also needs `LoggerService`, which the framework already has, so the framework can provide the same instance. `UserContextService` also needs `UserService`, which the framework has yet to create. `UserService` has no further dependencies, so the framework can simply use `new` to instantiate the class and provide the instance to the `UserContextService` constructor.
The parent `AppComponent` doesn't need to know about the dependencies of dependencies.
Declare what's needed in the constructor (in this case `LoggerService` and `UserContextService`)
and the framework resolves the nested dependencies.
When all dependencies are in place, `AppComponent` displays the user information.
<div class="lightbox">
<img src="generated/images/guide/dependency-injection-in-action/logged-in-user.png" alt="Logged In User">
</div>
{@a service-scope}
## Limit service scope to a component subtree
An Angular application has multiple injectors, arranged in a tree hierarchy that parallels the component tree.
Each injector creates a singleton instance of a dependency.
That same instance is injected wherever that injector provides that service.
A particular service can be provided and created at any level of the injector hierarchy,
which means that there can be multiple instances of a service if it is provided by multiple injectors.
Dependencies provided by the root injector can be injected into *any* component *anywhere* in the application.
In some cases, you might want to restrict service availability to a particular region of the application.
For instance, you might want to let users explicitly opt in to use a service,
rather than letting the root injector provide it automatically.
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*.
This example shows how to make a different instance of `HeroService` available to `HeroesBaseComponent`
by adding it to the `providers` array of the `@Component()` decorator of the sub-component.
<code-example path="dependency-injection-in-action/src/app/sorted-heroes.component.ts" region="injection" header="src/app/sorted-heroes.component.ts (HeroesBaseComponent excerpt)">
</code-example>
When Angular creates `HeroesBaseComponent`, it also creates a new instance of `HeroService`
that is visible only to that component and its children, if any.
You could also provide `HeroService` to a different component elsewhere in the application.
That would result in a different instance of the service, living in a different injector.
<div class="alert is-helpful"> <div class="alert is-helpful">
Examples of such scoped `HeroService` singletons appear throughout the accompanying sample code, See the <live-example></live-example> for a working example containing the code snippets in this guide.
including `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`.
Each of these components has its own `HeroService` instance managing its own independent collection of heroes.
</div> </div>
{@a multiple-service-instances} {@a multiple-service-instances}
## Multiple service instances (sandboxing) ## Multiple service instances (sandboxing)
Sometimes you 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.
@ -272,7 +196,7 @@ Although developers strive to avoid it, many visual effects and third-party tool
require DOM access. require DOM access.
As a result, you might need to access a component's DOM element. As a result, you might need to access a component's DOM element.
To illustrate, here's a simplified version of `HighlightDirective` from To illustrate, here's a minimal version of `HighlightDirective` from
the [Attribute Directives](guide/attribute-directives) page. the [Attribute Directives](guide/attribute-directives) page.
<code-example path="dependency-injection-in-action/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts"> <code-example path="dependency-injection-in-action/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts">
@ -297,46 +221,6 @@ The following image shows the effect of mousing over the `<hero-bios-and-contact
<div class="lightbox"> <div class="lightbox">
<img src="generated/images/guide/dependency-injection-in-action/highlight.png" alt="Highlighted bios"> <img src="generated/images/guide/dependency-injection-in-action/highlight.png" alt="Highlighted bios">
</div> </div>
{@a providers}
## Define dependencies with providers
This section demonstrates how to write providers that deliver dependent services.
In order to get a service from a dependency injector, you have to give it a [token](guide/glossary#token).
Angular usually handles this transaction by specifying a constructor parameter and its type.
The parameter type serves as the injector lookup token.
Angular passes this token to the injector and assigns the result to the parameter.
The following is a typical example.
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" header="src/app/hero-bios.component.ts (component constructor injection)"></code-example>
Angular asks the injector for the service associated with `LoggerService`
and assigns the returned value to the `logger` parameter.
If the injector has already cached an instance of the service associated with the token,
it provides that instance.
If it doesn't, it needs to make one using the provider associated with the token.
<div class="alert is-helpful">
If the injector doesn't have a provider for a requested token, it delegates the request
to its parent injector, where the process repeats until there are no more injectors.
If the search fails, the injector throws an error&mdash;unless the request was [optional](guide/dependency-injection-in-action#optional).
</div>
A new injector has no providers.
Angular initializes the injectors it creates with a set of preferred providers.
You have to configure providers for your own app-specific dependencies.
{@a defining-providers} {@a defining-providers}
@ -700,53 +584,3 @@ appear *above* the class definition.
Break the circularity with `forwardRef`. Break the circularity with `forwardRef`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)"></code-example> <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)"></code-example>
<!--- Waiting for good examples
{@a directive-level-providers}
{@a element-level-providers}
## Element-level providers
A component is a specialization of directive, and the `@Component()` decorator inherits the `providers` property from `@Directive`. The injector is at the element level, so a provider configured with any element-level injector is available to any component, directive, or pipe attached to the same element.
Here's a live example that implements a custom form control, taking advantage of an injector that is shared by a component and a directive on the same element.
https://stackblitz.com/edit/basic-form-control
The component, `custom-control`, configures a provider for the DI token `NG_VALUE_ACCESSOR`.
In the template, the `FormControlName` directive is instantiated along with the custom component.
It can inject the `NG_VALUE_ACCESSOR` dependency because they share the same injector.
(Notice that this example also makes use of `forwardRef()` to resolve a circularity in the definitions.)
### Sharing a service among components
__NEED TO TURN THIS INTO FULL EXTERNAL EXAMPLE__
Suppose you want to share the same `HeroCacheService` among multiple components. One way to do this is to create a directive.
```
<ng-container heroCache>
<hero-overview></hero-overview>
<hero-details></hero-details>
</ng-container>
```
Use the `@Directive()` decorator to configure the provider for the service:
```
@Directive(providers:[HeroCacheService])
class heroCache{...}
```
Because the injectors for both the overview and details components are children of the injector created from the `heroCache` directive, they can inject things it provides.
If the `heroCache` directive provides the `HeroCacheService`, the two components end up sharing them.
If you want to show only one of them, use the directive to make sure __??of what??__.
`<hero-overview heroCache></hero-overview>`
--->

View File

@ -238,11 +238,6 @@
"url": "guide/dependency-injection-providers", "url": "guide/dependency-injection-providers",
"title": "DI Providers", "title": "DI Providers",
"tooltip": "More about the different kinds of providers." "tooltip": "More about the different kinds of providers."
},
{
"url": "guide/dependency-injection-in-action",
"title": "DI in Action",
"tooltip": "Techniques for dependency injection."
} }
] ]
} }
@ -883,9 +878,20 @@
] ]
}, },
{ {
"url": "guide/hierarchical-dependency-injection", "title": "Dependency injection",
"title": "Hierarchical Injectors", "tooltip": "Using dependency injection in Angular.",
"tooltip": "An injector tree parallels the component tree and supports nested dependencies." "children": [
{
"url": "guide/hierarchical-dependency-injection",
"title": "Hierarchical Injectors",
"tooltip": "An injector tree parallels the component tree and supports nested dependencies."
},
{
"url": "guide/dependency-injection-in-action",
"title": "DI in Action",
"tooltip": "Techniques for dependency injection."
}
]
} }
] ]
}, },