w3resource

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.

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 for each component, of which that component's host view is the root.

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 VitaComponent has several children including an AryanComponent:

TypeScript Code:

@Component({
  selector: 'vita',
  template: `
    <div class="a">
      <h3>{{name}}</h3>
      <aryan></aryan>
      <craig></craig>
      <carol></carol>
    </div>`,
})
export class VitaComponent extends Base
{
  name = 'Vita';
}

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen parent-finder.ts(VitaComponent v.1) by w3resource (@w3resource) on CodePen.


Aryan reports whether or not he has access to Vita after injecting a VitaComponent into his constructor:

TypeScript Code:

@Component({
  selector: 'aryan',
  template: `
  <div class="a">
    <h3>Aryan</h3>
    {{vita ? 'Found' : 'Did not find'}} Vita via the component class.<br>
  </div>
})
export class AryanComponent {
  constructor( @Optional() public vita: VitaComponent ) { }
}

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen parent-finder.ts(AryanComponent) by w3resource (@w3resource) on CodePen.


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.

Looking back, you see that the Vita component extends (inherits) from a class named Base.

export class VitaComponent extends Base

The CraigComponent tries to inject Base into its vita constructor parameter and reports if it succeeded.

TypeScript Code:

@Component({
  selector: 'craig',
  template: `
  <div class="c">
    <h3>Craig</h3>
    {{alex ? 'Found' : 'Did not find'}} Vita via the base class.
  </div>`
})
export class CraigComponent {
  constructor( @Optional() public vita: Base ) { }
}

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen parent-finder.ts(CraigComponent) by w3resource (@w3resource) on CodePen.


Find a parent by its class interface

You can find a parent component with a 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 Vita into Cathy earlier.

Write an alias provider—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 provider's array of the @Component() metadata for the VitaComponent.

providers: [{ provide: Parent, useExisting: forwardRef(() => VitaComponent) }],

Parent is the provider's class interface token. The forwardRef breaks the circular reference you just created by having the VitaComponent refer to itself.

Carol, the third of Vita's child components, injects the parent into its parent parameter, the same way you've done it before. Below is the CarolComponent Class

export class CarolComponent {
  name = 'Carol';
  constructor( @Optional() public parent: Parent ) { }
}

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.

TypeScript Code:

const templateB = `
  <div class="b">
    <div>
      <h3>{{name}}</h3>
      <p>My parent is {{parent?.name}}</p>
    </div>
    <carol></carol>
    <chris></chris>
  </div>`;

@Component({
  selector:   'barry',
  template:   templateB,
  providers:  [{ provide: Parent, useExisting: forwardRef(() => BarryComponent) }]
})
export class BarryComponent implements Parent {
  name = 'Barry';
  constructor( @SkipSelf() @Optional() public parent: Parent ) { }
}

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen parent-finder.ts(BarryComponent) by w3resource (@w3resource) on CodePen.


Previous: Hierarchical dependency injectors
Next: Routing and Navigation



Follow us on Facebook and Twitter for latest update.