313 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			313 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | # Navigate the component tree with DI
 | ||
|  | 
 | ||
|  | Application components often need to share information. | ||
|  | You can often use loosely coupled techniques for sharing information, | ||
|  | such as data binding and service sharing,  | ||
|  | but sometimes it makes sense for one component to have a direct reference to another component.  | ||
|  | You need a direct reference, for instance, to access values or call methods on that component. | ||
|  | 
 | ||
|  | Obtaining a component reference is a bit tricky in Angular. | ||
|  | Angular components themselves do not have a tree that you can  | ||
|  | inspect or navigate programmatically. The parent-child relationship is indirect, | ||
|  | established through the components' [view objects](guide/glossary#view). | ||
|  | 
 | ||
|  | Each component has a *host view*, and can have additional *embedded views*.  | ||
|  | An embedded view in component A is the | ||
|  | host view of component B, which can in turn have embedded view. | ||
|  | This means that there is a [view hierarchy](guide/glossary#view-hierarchy) for each component, | ||
|  | of which that component's host view is the root. | ||
|  | 
 | ||
|  | There is an API for navigating *down* the view hierarchy. | ||
|  | Check out `Query`, `QueryList`, `ViewChildren`, and `ContentChildren` | ||
|  | in the [API Reference](api/). | ||
|  | 
 | ||
|  | There is no public API for acquiring a parent reference. | ||
|  | However, because every component instance is added to an injector's container, | ||
|  | you can use Angular dependency injection to reach a parent component. | ||
|  | 
 | ||
|  | This section describes some techniques for doing that. | ||
|  | 
 | ||
|  | {@a find-parent} | ||
|  | {@a known-parent} | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Find a parent component of known type
 | ||
|  | 
 | ||
|  | You use standard class injection to acquire a parent component whose type you know. | ||
|  | 
 | ||
|  | In the following example, the parent `AlexComponent` has several children including a `CathyComponent`: | ||
|  | 
 | ||
|  | {@a alex} | ||
|  | 
 | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-1" title="parent-finder.component.ts (AlexComponent v.1)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | *Cathy* reports whether or not she has access to *Alex* | ||
|  | after injecting an `AlexComponent` into her constructor: | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" title="parent-finder.component.ts (CathyComponent)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | Notice that even though the [@Optional](guide/dependency-injection-in-action#optional) qualifier | ||
|  | is there for safety, | ||
|  | the <live-example name="dependency-injection-in-action"></live-example> | ||
|  | confirms that the `alex` parameter is set. | ||
|  | 
 | ||
|  | 
 | ||
|  | {@a base-parent} | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Unable to find a parent by its base class
 | ||
|  | 
 | ||
|  | What if you *don't* know the concrete parent component class? | ||
|  | 
 | ||
|  | A re-usable component might be a child of multiple components. | ||
|  | Imagine a component for rendering breaking news about a financial instrument. | ||
|  | For business reasons, this news component makes frequent calls | ||
|  | directly into its parent instrument as changing market data streams by. | ||
|  | 
 | ||
|  | The app probably defines more than a dozen financial instrument components. | ||
|  | If you're lucky, they all implement the same base class | ||
|  | whose API your `NewsComponent` understands. | ||
|  | 
 | ||
|  | 
 | ||
|  | <div class="alert is-helpful"> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | Looking for components that implement an interface would be better. | ||
|  | That's not possible because TypeScript interfaces disappear | ||
|  | from the transpiled JavaScript, which doesn't support interfaces. | ||
|  | There's no artifact to look for. | ||
|  | 
 | ||
|  | </div> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | This isn't necessarily good design. | ||
|  | This example is examining *whether a component can | ||
|  | inject its parent via the parent's base class*. | ||
|  | 
 | ||
|  | The sample's `CraigComponent` explores this question. [Looking back](#alex), | ||
|  | you see that the `Alex` component *extends* (*inherits*) from a class named `Base`. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" title="parent-finder.component.ts (Alex class signature)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" title="parent-finder.component.ts (CraigComponent)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | Unfortunately, this does'nt work. | ||
|  | The <live-example name="dependency-injection-in-action"></live-example> | ||
|  | confirms that the `alex` parameter is null. | ||
|  | *You cannot inject a parent by its base class.* | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | {@a class-interface-parent} | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Find a parent by its class interface
 | ||
|  | 
 | ||
|  | You can find a parent component with a [class interface](guide/dependency-injection-in-action#class-interface). | ||
|  | 
 | ||
|  | The parent must cooperate by providing an *alias* to itself in the name of a class interface token. | ||
|  | 
 | ||
|  | Recall that Angular always adds a component instance to its own injector; | ||
|  | that's why you could inject *Alex* into *Cathy* [earlier](#known-parent). | ||
|  | 
 | ||
|  | Write an [*alias provider*](guide/dependency-injection-in-action#useexisting)—a `provide` object literal with a `useExisting` | ||
|  | definition—that creates an *alternative* way to inject the same component instance | ||
|  | and add that provider to the `providers` array of the `@Component()` metadata for the `AlexComponent`. | ||
|  | 
 | ||
|  | {@a alex-providers} | ||
|  | 
 | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" title="parent-finder.component.ts (AlexComponent providers)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | [Parent](#parent-token) is the provider's class interface token. | ||
|  | The [*forwardRef*](guide/dependency-injection-in-action#forwardref) breaks the circular reference you just created by having the `AlexComponent` refer to itself. | ||
|  | 
 | ||
|  | *Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, | ||
|  | the same way you've done it before. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" title="parent-finder.component.ts (CarolComponent class)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | Here's *Alex* and family in action. | ||
|  | 
 | ||
|  | <figure> | ||
|  |   <img src="generated/images/guide/dependency-injection-in-action/alex.png" alt="Alex in action"> | ||
|  | </figure> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | {@a parent-tree} | ||
|  | 
 | ||
|  | 
 | ||
|  | ### Find a parent in a tree with _@SkipSelf()_
 | ||
|  | 
 | ||
|  | Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*. | ||
|  | Both *Alice* and *Barry* implement the `Parent' class interface. | ||
|  | 
 | ||
|  | *Barry* is the problem. He needs to reach his parent, *Alice*, and also be a parent to *Carol*. | ||
|  | That means he must both *inject* the `Parent` class interface to get *Alice* and | ||
|  | *provide* a `Parent` to satisfy *Carol*. | ||
|  | 
 | ||
|  | Here's *Barry*. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" title="parent-finder.component.ts (BarryComponent)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | *Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers). | ||
|  | If you're going to keep writing [*alias providers*](guide/dependency-injection-in-action#useexisting) like this you should create a [helper function](#provideparent). | ||
|  | 
 | ||
|  | For now, focus on *Barry*'s constructor. | ||
|  | 
 | ||
|  | <code-tabs> | ||
|  | 
 | ||
|  |   <code-pane title="Barry's constructor" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry-ctor"> | ||
|  | 
 | ||
|  |   </code-pane> | ||
|  | 
 | ||
|  |   <code-pane title="Carol's constructor" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-ctor"> | ||
|  | 
 | ||
|  |   </code-pane> | ||
|  | 
 | ||
|  | </code-tabs> | ||
|  | 
 | ||
|  | 
 | ||
|  | It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator. | ||
|  | 
 | ||
|  | `@SkipSelf` is essential for two reasons: | ||
|  | 
 | ||
|  | 1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself, | ||
|  | which *is* what parent means. | ||
|  | 
 | ||
|  | 2. Angular throws a cyclic dependency error if you omit the `@SkipSelf` decorator. | ||
|  | 
 | ||
|  |   `Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)` | ||
|  | 
 | ||
|  | Here's *Alice*, *Barry*, and family in action. | ||
|  | 
 | ||
|  | 
 | ||
|  | <figure> | ||
|  |   <img src="generated/images/guide/dependency-injection-in-action/alice.png" alt="Alice in action"> | ||
|  | </figure> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | {@a parent-token} | ||
|  | 
 | ||
|  | 
 | ||
|  | ###  Parent class interface
 | ||
|  | You [learned earlier](guide/dependency-injection-in-action#class-interface) that a class interface is an abstract class used as an interface rather than as a base class. | ||
|  | 
 | ||
|  | The example defines a `Parent` class interface. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" title="parent-finder.component.ts (Parent class-interface)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | The `Parent` class interface defines a `name` property with a type declaration but *no implementation*. | ||
|  | The `name` property is the only member of a parent component that a child component can call. | ||
|  | Such a narrow interface helps decouple the child component class from its parent components. | ||
|  | 
 | ||
|  | A component that could serve as a parent *should* implement the class interface as the `AliceComponent` does. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-class-signature" title="parent-finder.component.ts (AliceComponent class signature)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | Doing so adds clarity to the code.  But it's not technically necessary. | ||
|  | Although `AlexComponent` has a `name` property, as required by its `Base` class, | ||
|  | its class signature doesn't mention `Parent`. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" title="parent-finder.component.ts (AlexComponent class signature)" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | <div class="alert is-helpful"> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | `AlexComponent` *should* implement `Parent` as a matter of proper style. | ||
|  | It doesn't in this example *only* to demonstrate that the code will compile and run without the interface. | ||
|  | 
 | ||
|  | 
 | ||
|  | </div> | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | {@a provideparent} | ||
|  | 
 | ||
|  | 
 | ||
|  | ### `provideParent()` helper function
 | ||
|  | 
 | ||
|  | Writing variations of the same parent *alias provider* gets old quickly, | ||
|  | especially this awful mouthful with a [*forwardRef*](guide/dependency-injection-in-action#forwardref). | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | You can extract that logic into a helper function like the following. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | Now you can add a simpler, more meaningful parent provider to your components. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | You can do better. The current version of the helper function can only alias the `Parent` class interface. | ||
|  | The application might have a variety of parent types, each with its own class interface token. | ||
|  | 
 | ||
|  | Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 | ||
|  | 
 | ||
|  | And here's how you could use it with a different parent type. | ||
|  | 
 | ||
|  | <code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false"> | ||
|  | 
 | ||
|  | </code-example> | ||
|  | 
 |