Thursday, August 5, 2021

Angular @ViewChildren Decorator With Examples

@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

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.

  1. Any class with the @Component or @Directive decorator
    @ViewChildren(UserComponent) users: QueryList<UserComponent>; 
    @ViewChildren(HighLightDirective) hdList: QueryList<HighLightDirective>;
    
  2. A template reference variable as a string. For example
    <user-component #usr></user-component>
    
    can be queried as
    @ViewChildren('usr')
    
  3. Any provider defined in the child component tree of the current component. For example
    @ViewChildren(LoginService) loginService!: LoginService)
    
  4. Any provider defined through a string token (e.g. @ViewChildren('someToken') someTokenVal!: any)
  5. 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.

ViewChildren with directive

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>
@ViewChildren

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 with template reference

@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

  1. Angular @ViewChild Decorator With Examples
  2. Angular @HostListener Decorator With Examples
  3. Angular @HostBinding Decorator With Examples
  4. Angular @Output() Decorator With Examples
  5. ng-template in Angular

You may also like-

  1. Angular Custom Two-Way Data Binding
  2. Path Redirection in Angular Routing
  3. Custom Pipe in Angular With Example
  4. Checkbox in Angular Form Example
  5. Parquet File Format in Hadoop
  6. Var type in Java - Local Variable Type Inference
  7. Spring Expression Language (SpEL) With Examples
  8. Spring Component Scan to Automatically Discover Beans

No comments:

Post a Comment