ViewChild, ViewChildren & QueryList In Angular
To Query and retrieve the reference of the DOM element in the Component, use the ViewChild or ViewChildren decorators. ViewChild returns the first matched element, while ViewChildren produces a QueryList of all matching elements. These references can be used to manipulate the component's element properties.
To query a DOM element(s), the first argument to the ViewChild or ViewChildren must be the query selector, which can be a string or a type. The static option controls whether the query is run before or after change detection. When the element is associated with various kinds, the read-option allows us to query a different token than the default. All of this will be covered in this article.
ViewChild
The ViewChild query retrieves the first matching element from the DOM and modifies the component variable we're using it on.
Syntax
The viewChild has the syntax indicated below.
ViewChild(selector: string | Function | Type<any>, opts: { read?: any; static: boolean; }): any
ViewChild Examples
Injecting Component or Directive Reference
import { Component } from '@angular/core'; @Component({ selector: 'child-component', template: `<h2>Child Component</h2> current count is {{ count }} ` }) export class ChildComponent { count = 0; increment() { this.count++; } decrement() { this.count--; } }
To gain a reference to the ChildComponent, we can use the ViewChild in the parent component.
@ViewChild(ChildComponent, {static:true}) child: ChildComponent;
import { Component, ViewChild } from '@angular/core'; import { ChildComponent } from './child.component'; @Component({ selector: 'app-root', template: ` <h1>{{title}}</h1> <p> current count is {{child.count}} </p> <button (click)="increment()">Increment</button> <button (click)="decrement()">decrement</button> <child-component></child-component> ` , styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'Parent calls an @ViewChild()'; @ViewChild(ChildComponent, {static:true}) child: ChildComponent; increment() { this.child.increment(); } decrement() { this.child.decrement(); } }
Using Template Reference Variable
<child-component #child></child-component>
After that, use it in the ViewChild query to acquire the component's reference.
@ViewChild("child", { static: true }) child: ChildComponent;
Injecting HTML Element Using ElementRef
import {AfterViewInit, Component, ElementRef, ViewChild} from '@angular/core'; @Component({ selector: 'htmlelement', template: ` <p #para>Some text</p> `, }) export class HTMLElementComponent implements AfterViewInit { @ViewChild('para',{static:false}) para: ElementRef; ngAfterViewInit() { console.log(this.para.nativeElement.innerHTML); this.para.nativeElement.innerHTML="new text" } }
Multiple Instances
<child-component></child-component> <child-component></child-component>
The first component is always returned by ViewChild.
@ViewChild(ChildComponent, {static:true}) child: ChildComponent;
ViewChild returns Undefined
export class AppComponent { title = 'Parent calls an @ViewChild()'; @ViewChild(ChildComponent, {static:true}) child: ChildComponent; constructor() { this.child.increment() } } // Cannot read property 'increment' of undefined
Wait until Angular Initializes the View before proceeding. Once the View Initialization is complete, Angular raises the AfterViewInit life cycle hook. As a result, we can access the child variable using ngAfterViewInit.
ngAfterViewInit() { this.child.increment() }
Using Static Option in ViewChild
- Before any change detection is conducted, static:true resolves ViewChild.
- After each change detection run, static:false will resolve it.
<h1>ViewChild Example</h1> <input type="checkbox" id="showCounter" name="showCounter" [(ngModel)]="showCounter"> <ng-container *ngIf="showCounter"> <p> current count is {{child?.count}} </p> <button (click)="increment()">Increment</button> <button (click)="decrement()">decrement</button> <child-component></child-component> </ng-container>
Add the following code under the child.component.ts:
import { Component, ViewChild, AfterViewInit, OnInit, ChangeDetectorRef } from '@angular/core'; import { ChildComponent } from './child.component'; @Component({ selector: 'app-root', templateUrl: 'app.component.html' , styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'ViewChild Example)'; showCounter: boolean = true @ViewChild(ChildComponent, { static: true }) child: ChildComponent; increment() { this.child.increment(); } decrement() { this.child.decrement(); } }
Using the Read Option in ViewChild
<input #nameInput [(ngModel)]="name">
The viewChild code below returns elementRef, which is the instance of the input element.
@ViewChild('nameInput',{static:false}) nameVar;
We use the Read token and ask for the type if we want to access the instance of the ngModel.
@ViewChild('nameInput',{static:false, read: NgModel}) inRef; @ViewChild('nameInput',{static:false, read: ElementRef}) elRef; @ViewChild('nameInput', {static:false, read: ViewContainerRef }) vcRef;
Injecting a Provider from the Child Component
import { ViewChild, Component } from '@angular/core'; @Component({ selector: 'app-child', template: `<h1>Child With Provider</h1>`, providers: [{ provide: 'Token', useValue: 'Value' }] }) export class ChildComponent{ }
The read property in the Parent component can be used to access the provider.
import { ViewChild, Component } from '@angular/core'; @Component({ selector: 'app-root', template: `<app-child></app-child>`, }) export class AppComponent{ @ViewChild(ChildComponent , { read:'Token', static:false } ) childToken: string; }
Injecting TemplateRef
<ng-template #sayHelloTemplate> <p> Say Hello</p> </ng-template>
Component code
@ViewChild("sayHelloTemplate", { static: false }) tempRef: TemplateRef;
ViewChildren
Syntax
ViewChildren(selector: string | Function | Type<any>, opts: { read?: any; }): any
QueryList
- first: returns the list's first item.
- last: retrieve the list's last item.
- length: find out how long the objects are.
- changes: Is anything that can be seen. When Angular adds, removes, or moves the child elements, it emits a new value.
ViewChildren Example
import { ViewChild, Component, ViewChildren, QueryList, AfterViewInit } from '@angular/core'; import { NgModel } from '@angular/forms'; @Component({ selector: 'app-viewchildren1', template: ` <h1>ViewChildren Example</h1> <input name="firstName" [(ngModel)]="firstName"> <input name="midlleName" [(ngModel)]="middleName"> <input name="lastName" [(ngModel)]="lastName"> <button (click)="show()">Show</button> `, }) export class ViewChildrenExample1Component { firstName; middleName; lastName; @ViewChildren(NgModel) modelRefList: QueryList<NgModel>; show() { this.modelRefList.forEach(element => { console.log(element) //console.log(element.value) }); } }
Listening for QueryList Changes
import { ViewChild, Component, ViewChildren, QueryList, AfterViewInit } from '@angular/core'; import { NgModel } from '@angular/forms'; @Component({ selector: 'app-viewchildren2', template: ` <h1>ViewChildren Example</h1> <input *ngIf="showFirstName" name="firstName" [(ngModel)]="firstName"> <input *ngIf="showMiddleName" name="midlleName" [(ngModel)]="middleName"> <input *ngIf="showlastName" name="lastName" [(ngModel)]="lastName"> <input type="checkbox" id="showFirstName" name="showFirstName" [(ngModel)]="showFirstName"> <input type="checkbox" id="showMiddleName" name="showMiddleName" [(ngModel)]="showMiddleName"> <input type="checkbox" id="showlastName" name="showlastName" [(ngModel)]="showlastName"> <button (click)="show()">Show</button> `, })
export class ViewChildrenExample2Component implements AfterViewInit { firstName; middleName; lastName; showFirstName=true; showMiddleName=true; showlastName=true; @ViewChildren(NgModel) modelRefList: QueryList<NgModel>; ngAfterViewInit() { this,this.modelRefList.changes .subscribe(data => { console.log(data) } ) } show() { this.modelRefList.forEach(element => { console.log(element) //console.log(element.value) }); } }