@ViewChild and @ViewChildren in Angular - Part 2
We discussed in the preview blog that the @ViewChild
and @ViewChildren
decorators in Angular are primarily used for querying elements in the template, but they can also be used for some other purposes beyond just selecting template elements. Here are a few scenarios where these decorators can be useful apart from querying the template:
- Component Communication:
We can use@ViewChild
and@ViewChildren
to establish communication between child and parent components. We can use@ViewChild
to get a reference to a child component and call methods or access properties on that component. - Accessing Child Component Instances:
We can use@ViewChild
to access instances of child components. This can be useful when we need to interact with child components or retrieve data from them. - Lifecycle Hook Manipulation:
We can use@ViewChild
to trigger lifecycle hooks in child components. For example, we can programmatically callngOnInit
,ngOnChanges
, or other lifecycle hooks on child components.
Accessing Child Component Instances and Communication b/w Components
Suppose we have a parent component (ParentComponent
) that includes a child component (ChildComponent
). We want to access the instance of ChildComponent
from ParentComponent
and interact with it.
import { Component } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<h2>Child Component</h2>
<button (click)="childMethod()">Call Child Method</button>
`,
})
export class ChildComponent {
childData: string = 'Data from Child';
childMethod() {
console.log('Child component method called');
}
}
import { Component, ViewChild } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
template: `
<h1>Parent Component</h1>
<p>{{ child.childData }}</p>
<button (click)="callChildMethod()">Call Child Method</button>
<app-child #child></app-child>
`,
})
export class ParentComponent {
@ViewChild('child', { static: true }) child: ChildComponent;
callChildMethod() {
this.child.childMethod(); // Call the child component's method
}
}
This example demonstrates how we can access the instance of a child component from the parent component and call methods on the child component or access its properties.
ParentComponent
uses the@ViewChild
decorator to access the instance ofChildComponent
. We specify the template reference variable#child
to identify the child component.callChildMethod
inParentComponent
is a method that calls a method defined in the child component,childMethod
.- The child component has a button that, when clicked, calls the
childMethod
.
With this setup, when we load the application, we’ll see the “Call Child Method” button in both the parent and child components. Clicking the button in the parent component will call the childMethod
in the child component, and we'll see the "Child component method called" message in the console.
Similarly, to our previous example, the @ViewChild
decorator is used to access a child component's instance and manipulate its lifecycle hooks. Let's explore an example of how to do this:
Suppose we have a parent component (ParentComponent
) with a child component (ChildComponent
). We want to programmatically trigger the ngOnInit
and ngOnChanges
lifecycle hooks of the child component from the parent component.
import { Component, OnInit, OnChanges, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-child',
template: `<h2>Child Component</h2>`,
})
export class ChildComponent implements OnInit, OnChanges {
ngOnChanges(changes: SimpleChanges): void {
console.log('ChildComponent - ngOnChanges', changes);
}
ngOnInit(): void {
console.log('ChildComponent - ngOnInit');
}
}
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
template: `
<h1>Parent Component</h1>
<button (click)="triggerChildLifecycleHooks()">Trigger Child Hooks</button>
<app-child #childComponent></app-child>
`,
})
export class ParentComponent implements AfterViewInit {
@ViewChild('childComponent') child: ChildComponent;
ngAfterViewInit() {
// Access the child component
}
triggerChildLifecycleHooks() {
// Programmatically trigger child component's lifecycle hooks
this.child.ngOnChanges({});
this.child.ngOnInit();
}
}
In this example:
- The
ChildComponent
has bothngOnInit
andngOnChanges
lifecycle hooks, which log messages to the console when they are triggered. - The
ParentComponent
uses@ViewChild
to access the instance ofChildComponent
and provides a button to trigger the child component's lifecycle hooks. - The
triggerChildLifecycleHooks
method in theParentComponent
programmatically calls thengOnChanges
andngOnInit
lifecycle hooks of the child component.
Note: Manipulating lifecycle hooks in this manner is not a common practice and should be used with caution. It’s essential to understand the implications of manually triggering these hooks and ensure that they align with our application’s requirements.
In summary, In Angular, we can enhance component interaction and control by utilizing techniques such as component communication via @ViewChild
and @ViewChildren
decorators to exchange data and trigger actions between parent and child components, accessing child component instances programmatically, and manipulating lifecycle hooks for fine-grained control. These approaches allow for flexible and interactive Angular applications that adapt to user interactions and changing requirements.