Most of the times when we need to change the behavior or appearance of an element in our template from our component class (i.e. from the TS file). we require a reference variable of that element. For this, in View (HTML) we can use template reference variable, but how can we get this variable into our component class?
For this, we can either use @ViewChild or @ViewChildren. Here we are going to understand when we need to use @ViewChild and @ViewChildren.
Let's understand this with an example.
Create a new component called “first” using the following command.
ng g c first
After that, write the following code in the “first.component.html” file.
<div class="row">
<button type="button" class="btn btn-primary">My Button</button>
</div>
If we want to take the reference of this element (i.e. button) in our component class, then we have to declare a local reference variable.
<div class="row">
<button type="button" class="btn btn-primary" #myButton>My Button</button>
</div>
Now, in our component class, to get the reference of this button, we use the @ViewChild decorator function.
@ViewChild("myButton") myValue: ElementRef;
Look at the picture to understand the above line.
@ViewChild decorator means to search for that element inside the component’s template. The parameter we passed as the first argument in the ViewChild, is the type of the component or the reference we want to search for. If it finds more than one it returns us the first one that it found.
@ViewChild returns a reference of an element so, we can use “ElementRef” to define the type of variable.
Let's print the above variable (‘myValue’) into the console.
@ViewChild("myButton") myValue: ElementRef;
constructor() {
console.log(this.myValue);
}
Save and run the application and see the output.
As we have seen in the above output, if we want to print this reference into the constructor, then it shows undefined. Now, the question is, Why did it show undefined inside the constructor and how can we use it?
It showed undefined inside the constructor because, at the time when the constructor was called, angular didn't render the children.
As we know, all the data is rendered in a Tree-Down approach in angular, so, when a parent component is constructed it means the child is not created yet.
So, to get the value of this reference we require a component hook (which we have already discussed in the component lifecycle hook article).
Here, we use either ngAfterViewInit or ngAfterViewChecked hook. Now, in this, we are going to use ngAfterViewInIt and inside this hook method, we will print the value of reference variable like.
ngAfterViewInit() {
console.log(this.myValue.nativeElement);
}
Now, we can say that we can easily access our reference variable with the help of @ViewChild. But, what can we do if we want to get the ViewChild of any component that has been used within this component? (To get the reference of the child component into the parent component) Like,
First, generate one more component called “clock”, by the following command.
ng g c clock
Now, to create the Clock component as a child of the First Component called the “app-clock” selector inside the “first.component.html” file.
<div class="container">
<div class="row mt-2 mb-2 text-center">
<div class="col-lg-12 col-md-12 col-sm-12">
<app-clock></app-clock>
</div>
</div>
<div class="row mt-2 mb-2 text-center">
<div class="col-lg-12 col-md-12 col-sm-12 col-12">
<button type="button" class="btn btn-primary" #myButton>My Button</button><br>
</div>
</div>
</div>
Go to the “clock.component.ts” file, and declare a variable of date type like.
today: Date = new Date();
Now, go to the “clock.component.html” file and write the following code.
<div class="clock bg-danger text-white">{{ today | date:'mediumTime' }}</div>
See, the output.
Now, if we want to access this property (today) of child component into parent then how can we do this?
For this, we create a reference of the child element into the parent component class. For this, we need to follow the following steps.
Step 1. Import the child component (clock) into the parent component (first.component.ts) file.
import { ClockComponent } from './../clock/clock.component';
Step 2. Create a component type reference with the help of @ViewChild() like.
@ViewChild(ClockComponent) myValue: ClockComponent;
Step 3. Use the child variable inside ngAfterViewInit() inside the parent’s component. Like this.
ngAfterViewInit() {
setInterval(() => {
this.myValue.today = new Date();
}, 1000);
} // use setInterval, so that time will change (a running clock)
See the above output, we change the time in the parent’s component by setInterval. Basically, when we run it, it runs a clock. I hope so far you understand what @ViewChild is and when to use it.
What is @ViewChildren?
Now, what do we need to do if we want to access a value from multiple child components? Like this.
<div class="container">
<div class="row mt-2 mb-2 text-center">
<div class="col-lg-12 col-md-12 col-sm-12">
<app-clock></app-clock> <!-- Child component (Use 1st time) -->
</div>
</div>
<div class="row mt-2 mb-2 text-center">
<div class="col-lg-12 col-md-12 col-sm-12">
<app-clock></app-clock> <!-- Child component (Use 2nd time) -->
</div>
</div>
<div class="row mt-2 mb-2 text-center">
<div class="col-lg-12 col-md-12 col-sm-12 col-12">
<button type="button" class="btn btn-primary" #myButton>My Button</button><br>
</div>
</div>
</div>
See the output.
Now, in above output, two elements rendered and we are able to see two clocks, but in the console only one reference is present. So, now to get multiple children components we can use @ViewChildren() decorator.
Let’s understand this by an example.
Go to the first.component.ts file and change “ViewChild()” to “ViewChildren()” like.
@ViewChildren(ClockComponent) myValue: QueryList<ClockComponent>;
Here, @ViewChildren will return a list of elements, so, we also require QueryList, to get the list of this type of component. See the output.
In the above output, at console screen, we are able to see the list inside the QueryList.
For more readability, we convert it into an array like this.
ngAfterViewInit() {
console.log(this.myValue.toArray());
}
See the output.
Conclusion
@ViewChild() provides the instance of another component or directive in a parent component and then the parent component can access the methods and properties of that component or directive. In this way, by using @ViewChild() a component can communicate with another component or a directive. But if we want to access multiple child references then we have to use @ViewChildren.
Now, we have learned about @ViewChild and @ViewChildren from this article. For practice purposes, here, a sample project called “SampleViewChild” is attached which covers all the code. You can download and modify this as per your requirements. All your queries related to this article and sample project are always welcome. Thanks for reading.