Angular @ViewChildren Example
February 11, 2024
Angular @ViewChildren
Decorator is used to get the QueryList
of elements or directives from the view DOM. When a new child element is added or removed, the QueryList
will be updated and its changes
function will emit new value. @ViewChildren
sets the data before ngAfterViewInit
callback. @ViewChildren
has following metadata properties.
selector: Selector for querying.
read: It is used to read different token from the queried elements.
@ViewChildren
supports following selector.
1. A Component class
@ViewChildren(WriterComponent) writers: QueryList<WriterComponent>;
@ViewChildren(MessageDirective) private msgList: QueryList<MessageDirective>;
@ViewChildren('bkWriter') writers: QueryList<WriterComponent>; @ViewChildren('pname') persons: QueryList<ElementRef>;
read
metadata.
@ViewChildren(WriterComponent, { read: ElementRef }) writers: QueryList<ElementRef>; @ViewChildren(MessageDirective, {read: ViewContainerRef}) msgList: QueryList<ViewContainerRef>;
@ViewChildren
complete example with Component
, Directive
and Template Reference Variable.
Contents
@ViewChildren with Component
To use@ViewChildren
with Component, we need to pass Component name or template reference variable as selector to @ViewChildren
.
Suppose we have a component WriterComponent
. We can use QueryList
of WriterComponent
in any other component using @ViewChildren
.
@ViewChildren(WriterComponent) writers1: QueryList<WriterComponent>;
read
metadata. Here we will read ElementRef
from the queried elements.
@ViewChildren(WriterComponent, {read: ElementRef}) writers2: QueryList<ElementRef>;
ViewContainerRef
from the queried elements.
@ViewChildren(WriterComponent, {read: ViewContainerRef}) writers3: QueryList<ViewContainerRef>;
QueryList
inside ngAfterViewInit
method because @ViewChildren
sets the data before AfterViewInit
. Now find the complete code.
vc-demo1.component.ts
import { Component, ViewChildren, AfterViewInit, ViewContainerRef, QueryList, ElementRef } from '@angular/core'; import { WriterComponent } from './writer.component'; @Component({ selector: 'app-vc-demo1', template: ` <h3>@ViewChildren + Component</h3> <div> <writer name="Krishna" book="Angular Tutorials"></writer> <br/> <writer name="Mahesh" book="Java Tutorials"></writer> <br/> <writer name="Krishna" book="jQuery Tutorials"></writer> <br/> <writer name="Bramha" book="Hibernate Tutorials" *ngIf="allWritersVisible"></writer> <br/> <writer name="Vishnu" book="Spring Tutorials" *ngIf="allWritersVisible"></writer> </div> <button (click)="onShowAllWriters()" > <label *ngIf="!allWritersVisible">Show More</label> <label *ngIf="allWritersVisible">Show Less</label> </button> ` }) export class VCOneDemoComponent implements AfterViewInit { @ViewChildren(WriterComponent) writers1: QueryList<WriterComponent>; @ViewChildren(WriterComponent, { read: ElementRef }) writers2: QueryList<ElementRef>; @ViewChildren(WriterComponent, { read: ViewContainerRef }) writers3: QueryList<ViewContainerRef>; allWritersVisible = false; ngAfterViewInit() { console.log('--- @ViewChildren + Component ---'); this.writers1.changes.subscribe(list => { list.forEach(writer => console.log(writer.writerName + ' - ' + writer.bookName)); }); console.log(this.writers1.length); console.log("Result with ElementRef:"); this.writers2.forEach(el => console.log(el)); console.log("Result with ViewContainerRef:"); this.writers3.forEach(vref => console.log(vref)); } onShowAllWriters() { this.allWritersVisible = (this.allWritersVisible === true) ? false : true; } }
import { Component, Input } from '@angular/core'; @Component({ selector: 'writer', template: ` {{writerName}} - {{bookName}} ` }) export class WriterComponent { @Input('name') writerName: string; @Input('book') bookName: string; }
QueryList
will be changed and this change can be observed using QueryList.changes
.
We can also use template reference variable with
@ViewChildren
to obtain QueryList
of Component. Suppose we have following HTML code.
<writer name="Krishna" book="Angular Tutorials" #bkWriter></writer> <br/> <writer name="Mahesh" book="Java Tutorials" #bkWriter></writer> <br/> <writer name="Krishna" book="jQuery Tutorials" #bkWriter></writer> <br/>
bkWriter
as template reference variable for WriterComponent
. Now we will query data as following.
@ViewChildren('bkWriter') writers1: QueryList<WriterComponent>;
@ViewChildren with Directive
We will query Directive using@ViewChildren
and embed a message using createEmbeddedView
of ViewContainerRef
class.
vc-demo2.component.ts
import { Component, ViewChild, ViewChildren, AfterViewInit, ViewContainerRef, TemplateRef, QueryList } from '@angular/core'; import { MessageDirective } from './message.directive'; @Component({ selector: 'app-vc-demo2', template: ` <h3>@ViewChildren + Directive</h3> <div cpMsg></div> <div cpMsg></div> <div cpMsg></div> <ng-template #msgTemp> Hello World! </ng-template> ` }) export class VCTwoDemoComponent implements AfterViewInit { @ViewChildren(MessageDirective) private msgList: QueryList<MessageDirective>; @ViewChild('msgTemp') private msgTempRef: TemplateRef<any>; ngAfterViewInit() { console.log('--- @ViewChildren + Directive ---'); console.log("this.msgList.length: " + this.msgList.length); this.msgList.forEach(messageDirective => messageDirective.viewContainerRef.createEmbeddedView(this.msgTempRef)); } }
read
metadata to read ViewContainerRef
directly from the directive.
@ViewChildren(MessageDirective, {read: ViewContainerRef}) private msgList: QueryList<ViewContainerRef>; ngAfterViewInit() { this.msgList.forEach(vcRef => vcRef.createEmbeddedView(this.msgTempRef)); }
message.directive.ts
import { Directive, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[cpMsg]' }) export class MessageDirective { constructor(public viewContainerRef: ViewContainerRef) { } }
@ViewChildren with ElementRef
We will use@ViewChildren
with ElementRef
here.
vc-demo3.component.ts
import { Component, ViewChildren, AfterViewInit, QueryList, ElementRef } from '@angular/core'; @Component({ selector: 'app-vc-demo3', template: ` <h3>@ViewChildren + ElementRef</h3> <div> <div #pname>Mohit</div> <div #pname>Anup</div> <div #pname>Nilesh</div> </div> ` }) export class VCThreeDemoComponent implements AfterViewInit { @ViewChildren('pname') persons: QueryList<ElementRef>; ngAfterViewInit() { console.log('--- @ViewChildren + ElementRef ---'); this.persons.forEach(el => console.log(el.nativeElement.innerHTML)); } }
ElementRef
.
Now find the other files used in the demo.
app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <app-vc-demo1></app-vc-demo1> <app-vc-demo2></app-vc-demo2> <app-vc-demo3></app-vc-demo3> ` }) export class AppComponent { }
@ViewChild vs @ViewChildren vs @ContentChild vs @ContentChildren
@ViewChild
and @ViewChildren
query the elements from view DOM and @ContentChild
and @ContentChildren
query the elements from content DOM.
1
@ViewChild
queries a single element or directive. It will be first element or the Directive matching the selector from the view DOM. It is used as following.
@ViewChild(WriterComponent) writer: WriterComponent; @ViewChild(MessageDirective) message: MessageDirective;
@ViewChildren
is used to get the QueryList
of elements or directives from the view DOM. It is used as following.
@ViewChildren(WriterComponent) writers: QueryList<WriterComponent>; @ViewChildren(MessageDirective) private msgList: QueryList<MessageDirective>;
@ContentChild
gives the first element or directive matching the selector from the content DOM. It is used as following.
@ContentChild(BookDirective) book: BookDirective;
@ContentChildren
is used to get QueryList
of elements or directives from the content DOM. It is used as following.
@ContentChildren(BookDirective) books: QueryList<BookDirective>