@ViewChildren decorator in Angular is used to get reference list of elements or directives from the view DOM in a Component class. Using @ViewChildren decorator you can configure a view query, that is done by passing a selector with @ViewChildren. For example
@ViewChildren('uname') userName: QueryList<UserComponent>
Here 'uname' is the passed selector. ViewChildren looks for all the elements or the directives matching the selector in the view DOM and return them as an instance of QueryList which is another class in Angular used to store unmodifiable list of items.
If a child element is added, removed, or moved, the query list will be updated, and the changes function of the QueryList will emit a new value.
Once you have access to DOM elements in the parent Component you can do DOM manipulation.
- View queries and ngAfterViewInit callback
- @ViewChildren Metadata Properties
- Which selectors are supported
- Using ViewChildren with directive
- Using ViewChildren with Component
- Using ViewChildren with ‘read’ property
- Using ViewChildren with template reference variable
- ViewChildren with multiple selectors
- @ViewChildren Vs @ViewChild
View queries and ngAfterViewInit callback
View queries are set before the ngAfterViewInit callback is called. That means ngAfterViewInit callback is the best place to manipulate the element or directive by using the reference variable.
@ViewChildren Metadata Properties
You can pass the following three metadata properties with @ViewChildren decorator.
- selector- The directive type or the name used for querying.
- read- Used to read a different token from the queried elements. It is an optional property.
- emitDistinctChangesOnly- When used the changes method of the QueryList will emit new values only if the QueryList result has changed. Note that this config option is deprecated, right now it is true by default and will be permanently set to true .
Syntax of ViewChildren-
@ViewChildren(selector, { read?: any;})
Which selectors are supported
@ViewChildren supports following selector.
- Any class with the @Component or @Directive decorator
@ViewChildren(UserComponent) users: QueryList<UserComponent>; @ViewChildren(HighLightDirective) hdList: QueryList<HighLightDirective>;
-
A template reference variable as a string. For example
<user-component #usr></user-component>
can be queried as@ViewChildren('usr')
-
Any provider defined in the child component tree of the current component. For example
@ViewChildren(LoginService) loginService!: LoginService)
- Any provider defined through a string token (e.g. @ViewChildren('someToken') someTokenVal!: any)
- A TemplateRef (e.g. query <ng-template></ng-template> with @ViewChildren(TemplateRef) template;)
@ViewChildren decorator Angular examples
In this section we’ll see some examples of ViewChildren to get an idea how you can use it with different selectors.
Using ViewChildren with directive
In this example there is a custom directive that highlights the content where it is used. Our logic is to highlight the content using default color if directive is not used more than 2 times otherwise highlight the content using the passed color. For that we can query Directive using @ViewChildren and check the length of the retuned QueryList instance.
customdirective.directive.ts
import { Directive, ElementRef, Renderer2 } from '@angular/core'; @Directive({ selector: '[appCustomdirective]' }) export class CustomdirectiveDirective { defaultColor: string = 'yellow'; constructor(private el: ElementRef, private renderer: Renderer2) { this.highLight(this.defaultColor); } highLight(color : string){ this.renderer.setStyle(this.el.nativeElement,'backgroundColor',color); } }
app.component.html
<p>Highlighting is done using a <span appCustomdirective>custom directive</span></p> <p appCustomdirective>Highlight the content</p>
app.component.ts
import { AfterViewInit, Component, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef } from '@angular/core'; import { CustomdirectiveDirective } from './directives/customdirective.directive'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements AfterViewInit{ @ViewChildren(CustomdirectiveDirective) private hdList: QueryList<CustomdirectiveDirective>; ngAfterViewInit(): void { console.log("this.msgList.length: " + this.hdList.length); if(this.hdList.length > 2) this.hdList.forEach(el => el.highLight('red')) } }
As you can see here custom Directive is queried using @ViewChildren, in the ngAfterViewInit() callback highLight() method of the directive is called if the length of returned query list is more than 2.
If directive is used more than 2 times then the HTML changes are as given below.
app.component.html
<p>Highlighting is done using a <span appCustomdirective>custom directive</span></p> <p appCustomdirective>Highlight the content</p> <div appCustomdirective>Highlight this content too</div>
Using ViewChildren with Component
In this example we’ll show user details where we have a Parent component named UsersComponent that passes each user to Child component named UserComponet which displays that data. We’ll query the UserComponet (child component) in the UsersComponent (parent component) to manipulate the data using the user reference.
user.model.ts
export class User { name : string; age : number; joinDate : Date; constructor(name: string, age : number, joinDate : Date) { this.name = name; this.age = age; this.joinDate = joinDate; } }
user.component.ts
import { Component, OnInit, Input } from '@angular/core'; import { User } from '../model/user.model'; @Component({ selector: 'app-user', templateUrl: './user.component.html' }) export class UserComponent { @Input() usr: User; }
user.component.html
<div class="container"> <div class="row"> <div class="col-xs-6"> <label>Name: </label> {{ usr.name }} <label>Age: </label> {{ usr.age }} <label>Join Date: </label> {{ usr.joinDate | date:'dd/MM/yyyy' }} </div> </div> </div>
users.component.ts
import { AfterViewInit, Component, QueryList, ViewChildren } from '@angular/core'; import { User } from '../bindings/user.model'; import { UserComponent } from './user.component'; @Component({ selector: 'app-users', templateUrl: './users.component.html' }) export class UsersComponent implements AfterViewInit{ @ViewChildren(UserComponent) userComponent: QueryList<UserComponent>; users: User[]; userData: User; constructor(){ //Adding User instances to users array this.users = [new User('Jack', 56, new Date('2005-03-25')), new User('Lisa', 32, new Date('2012-05-09')), new User('Jayesh', 28, new Date('2014-10-21'))] ; } ngAfterViewInit() { // Accessing all users using QueryList#forEach this.userComponent.forEach(user => console.log("User data " + user.usr.name + " " + user.usr.age)) // Accessing first user using QueryList#get this.userData = this.userComponent.get(0).usr; console.log('First User : ' + this.userData.name); // Changing the user name this.userData.name = "John"; } }
users.component.html
<div class="container"> <h3>User Details</h3> <app-user *ngFor="let user of users" [usr]="user"> </app-user> </div>Which gives you the following data in the console-
User data Jack 56 User data Lisa 32 User data Jayesh 28 First User : Jack
Using ViewChildren with ‘read’ property
You can use read option to read a different token from the queried element. In the example used above apart from Component if you want to read ElementRef from the queried element.
import { AfterViewInit, Component, ElementRef, QueryList, ViewChildren } from '@angular/core'; import { User } from '../bindings/user.model'; import { UserComponent } from './user.component'; @Component({ selector: 'app-users', templateUrl: './users.component.html', }) export class UsersComponent implements AfterViewInit{ @ViewChildren(UserComponent) userComponent: QueryList<UserComponent>; @ViewChildren(UserComponent, {read: ElementRef}) userList: QueryList<ElementRef>; users: User[]; userData: User; constructor(){ //Adding User instances to users array this.users = [new User('Jack', 56, new Date('2005-03-25')), new User('Lisa', 32, new Date('2012-05-09')), new User('Jayesh', 28, new Date('2014-10-21'))] ; } ngAfterViewInit() { // Accessing all users using QueryList#forEach this.userComponent.forEach(user => console.log("User data " + user.usr.name + " " + user.usr.age)) // Accessing first user using QueryList#get this.userData = this.userComponent.get(0).usr; console.log('First User : ' + this.userData.name); // Changing the user name this.userData.name = "John"; console.log("Using ElementRef to get HTML"); this.userList.forEach(e => console.log(e.nativeElement.innerHTML)); } }
In the log that gives the innerHTML as shown here-
Using ElementRef to get HTML <div class="container"><div class="row"><div class="col-xs-6"><label>Name: </label> Jack <label>Age: </label> 56 <label>Join Date: </label> 25/03/2005 </div></div></div> <div class="container"><div class="row"><div class="col-xs-6"><label>Name: </label> Lisa <label>Age: </label> 32 <label>Join Date: </label> 09/05/2012 </div></div></div> <div class="container"><div class="row"><div class="col-xs-6"><label>Name: </label> Jayesh <label>Age: </label> 28 <label>Join Date: </label> 21/10/2014 </div></div></div>
Using ViewChildren with template reference variable
You can also pass a template reference variable as selector to ViewChildren. In the example <h2> element is also assigned a template reference variable which is then accessed in the Component class using @ViewChildren and the font color for the element is changed.
app.component.html
<h2 #heading>View Children example</h2> <p #con>This is first line</p> <h2 #heading>Angular example</h2> <p #con>This is second line</p>
app.component.ts
import { AfterViewInit, Component, ElementRef, QueryList, ViewChildren} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements AfterViewInit{ @ViewChildren('heading') el: QueryList<ElementRef>; ngAfterViewInit(){ this.el.forEach(element => { element.nativeElement.style.color = 'yellow'; }); } }
ViewChildren with multiple selectors
You can also pass multiple selector separated with a comma. In the HTML used in the above example for <p> element also template reference variable is assigned and we will pass both of these as selector in the ViewChildren and based on the local name of the element we’ll assign different font colors.
import { AfterViewInit, Component, ElementRef, QueryList, ViewChildren} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements AfterViewInit{ @ViewChildren('heading, con') el: QueryList<ElementRef>; ngAfterViewInit(){ this.el.forEach(element => { if(element.nativeElement.localName === 'h2'){ element.nativeElement.style.color = 'green'; }else if(element.nativeElement.localName === 'p'){ element.nativeElement.style.color = 'red'; } }); } }
@ViewChildren Vs @ViewChild
Both @ViewChildren and @ViewChild decorators work similarly in the way that they both provide reference to elements or directives from the view DOM. How they differ is @ViewChild provides a single reference where as @ViewChildren provides a list of element references as an instance of QueryList.
That's all for this topic Angular @ViewChildren Decorator With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!
Related Topics
You may also like-
No comments:
Post a Comment