days
-1
-3
hours
-1
-3
minutes
-3
0
seconds
-5
-1
search

Simplifying ViewChild and ContentChild in Angular

Dhananjay Kumar
ViewChild
© Shutterstock / Unconventional

ViewChild and ContentChild are two very important features of Angular that are used to access Child Component in the Parent Component. In this tutorial, Dhananjay Kumar explains how you can use them for component communication in Angular.

In this blog post, we will learn about ViewChild and ContentChild in Angular.

Essentially ViewChild and ContentChild are used for component communication in Angular. Therefore, if a parent component wants access of child component then it uses ViewChild or ContentChild.

Any component, directive, or element which is part of a template is ViewChild and any component or element which is projected in the template is ContentChild.

ViewChild and ViewChildren

If you want to access following inside the Parent Component, use @ViewChild decorator of Angular.

  1. Child Component
  2. Directive
  3. DOM Element

ViewChild returns the first element that matches the selector.

Let us assume that we have a component MessageComponent as shown in the below listing:

import { Component, Input } from '@angular/core';
@Component({
    selector: 'app-message',
    template: `
<h2>{{message}}</h2>

`
})
export class MessageComponent {
    @Input() message: string;
 
} 

We are using MessageComponent inside AppComponent as shown in below listing:

import { Component, OnInit } from '@angular/core';
@Component({
    selector: 'app-root',
    template: `
  
<div>
  
<h1>Messages</h1>

  <app-message [message]='message'></app-message>
  </div>

`
})
export class AppComponent implements OnInit {
    message: any;
    ngOnInit() {
        this.message = 'Hello World !';
    }
} 

In application, you will get the output as below:

Here, MessageComponent has become child of AppComponent. Therefore, we can access it as a ViewChild. Definition of ViewChild is:

The Child Element which is located inside the component template”,

SEE MORE: How to dynamically create a component in Angular

Here MessageComponent is located inside template of AppComponent, so it can be accessed as ViewChild.

export class AppComponent implements OnInit, AfterViewInit {
    message: any;
    @ViewChild(MessageComponent) messageViewChild: MessageComponent;
 
    ngAfterViewInit() {
        console.log(this.messageViewChild);
    }
 
    ngOnInit() {
        this.message = 'Hello World !';
    }
} 

We need to do following tasks:

  • Import ViewChild and AfterViewInit from @angular/core
  • Implement AfterViewInit life cycle hook to component class
  • Create a variable with decorator @ViewChild
  • Access that inside ngAfterViewInit life cycle hook

In the output console you will find reference of MessageComponent, also if you can notice that __proto__ of MessageComponnet is set to Object.

Now let us try to change value of MessageComponent property:

ngAfterViewInit() {
    console.log(this.messageViewChild);
    this.messageViewChild.message = 'Passed as View Child';
} 

Here we are changing the value of ViewChild property, you will notice that value has been changed and you are getting output as below:

However, in the console you will find an error: “Expression has changed after it was last checked”.

This error can be fixed two ways,

  1. By changing the ViewChild property in ngAfterContentInit life cycle hook
  2. Manually calling change detection using ChangeDetectorRef

To fix it in ngAfterContentInit life cycle hook you need to implement AfterContentInit interface.

ngAfterContentInit() {
    this.messageViewChild.message = 'Passed as View Child';
} 

SEE MORE: Putting Angular and progressive web apps to work in 2018

Only problem with this approach is when you work with more than one ViewChild also known as ViewChildren. Reference of ViewChildren is not available in ngAfterContnetInit life cycle hook. In that case, to fix the above error, you will have to use a change detection mechanism. To use the change detection mechanism:

  1. Import ChangeDetectorRef from @angular/core
  2. Inject it to the constructor of Component class
  3. Call detectChanges() method after ViewChild property is changed

You can use manual change detection like shown in below listing:

constructor(private cd: ChangeDetectorRef) {}
 
ngAfterViewInit() {
    console.log(this.messageViewChild);
    this.messageViewChild.message = 'Passed as View Child';
    this.cd.detectChanges();
} 

Manually calling change detection will fix “Expression has changed after it was last checked,” error and it can be used with ViewChildren also.

To understand ViewChildren, let us consider AppComponent class created as shown in below listing:

import { Component, OnInit } from '@angular/core';
 
@Component({
    selector: 'app-root',
    template: `
  
<div>
  
<h1>Messages</h1>

  <app-message *ngFor="let f of messages" [message]='f'></app-message>
  </div>

`
})
export class AppComponent implements OnInit {
    messages: any;
    ngOnInit() {
        this.messages = this.getMessage();
    }
    getMessage() {
        return [
            'Hello India',
            'Which team is winning Super Bowl? ',
            'Have you checked Ignite UI ?',
            'Take your broken heart and make it to the art'
        ];
    }
} 

SEE MORE: On the road to Angular v6: What’s up with the release date?

We are using MessageComponent inside a *ngFor directive hence there are multiple references of MessageComponent. We can access it now as ViewChildren and QueryList as shown in the listing below:

@ViewChildren(MessageComponent) messageViewChildren: QueryList<MessageComponent>;
ngAfterViewInit() {
    console.log(this.messageViewChildren);
} 

To work with ViewChildren and QueryList, you need to do following tasks:

  • Import ViewChildren , QueryList , AfterViewInit from @angular/core
  • Make reference of ViewChildren with type QueryList
  • Access ViewChildren reference in ngAfterViewInit() life cycle hook

In the output, you will get various reference of MessageComponent as ViewChildern as shown in the image below:

Now let us try to update properties of ViewChildren as shown in the listing below:

ngAfterViewInit() {
    console.log(this.messageViewChildren);
    this.messageViewChildren.forEach((item) => { item.message = 'Infragistics'; });
} 

As you see, we are iterating through each item of ViewChildren and updating each property. This will update property value but again you will get the error, “Expression has changed after it was last checked” as shown in the image below:

SEE MORE: Two years with Angular and loving it

You can again fix it by manually calling change detection like ViewChild. Keep in mind that we do not have ViewChildren reference available in AfterContentInit life cycle hook. You will get undefined in ngAfterContentInit() life cycle hook for ViewChildren reference as shown in the listing below:

ngAfterContentInit() {
    console.log(this.messageViewChildren); // undefined 
} 

However, you can manually call change detection to fix error: “Expression has changed after it was last checked.

To use a change detection mechanism

  1. Import ChangeDetectorRef from @angular/core
  2. Inject it to the constructor of Component class
  3. Call detectChanges() method after ViewChild property is changed

You can use a manual change detection like shown in below listing:

@ViewChildren(MessageComponent) messageViewChildren: QueryList<MessageComponent>;
constructor(private cd: ChangeDetectorRef) {
}
ngAfterViewInit() {
    console.log(this.messageViewChildren);
    this.messageViewChildren.forEach((item) => { item.message = 'Infragistics'; });
    this.cd.detectChanges();
} 

In this way, you can work with ViewChild and ViewChildren.

SEE MORE: Stack Overflow survey: Developers love TensorFlow & React, dread Hadoop & Angular

ContentChild and ContnetChildren

Let us start with understanding about ContnetChild. Any element which is located inside the template, is ContnetChild.

To understand it let us consider MessageContainerComponent.

import { Component } from '@angular/core';
@Component({
    selector: 'app-messagecontainer',
    template: `
  
<div>
  
<h3>{{greetMessage}}</h3>

  <ng-content select="app-message"></ng-content>
  </div>

  `
})
export class MessageContainerComponent {
    greetMessage = 'Ignite UI Rocks!';
} 

In this component, we are using Angular Content Projection. You can learn more about content projection here.

Any element or component projected inside becomes a ContentChild. If you want to access and communicate with MessageComponent projected inside MessageContainerComponent, you need to read it as ContnetChild.

Before we go ahead and learn to use ContentChild, first see how MessageContainerComponent is used and MessageComponent is projected,

import { Component, OnInit } from '@angular/core';
@Component({
    selector: 'app-root',
    template: `
  
<div>
  <app-messagecontainer>
  <app-message [message]='message'></app-message>
  </app-messagecontainer>
  </div>

`
})
export class AppComponent implements OnInit {
    message: any;
    ngOnInit() {
        this.message = 'Hello World !';
    }
} 

As you see in the above listing that in the AppComponent, we are using MessageContainerComponent and passing MessageComponent to be projected inside it. Since MessageComponent is used in MessageContainerComponent using content projection, it becomes ContentChild.

SEE MORE: The ‘original’ Angular: Still kicking in 2018

Now, you will get output as shown below:

Since, MessageComponnet is projected and being used inside template of MessageContainerComponent, it can be used as ContentChild as shown in the below listing:

import { Component, ContentChild, AfterContentInit } from '@angular/core';
import { MessageComponent } from './message.component';
 
@Component({
    selector: 'app-messagecontainer',
    template: `
  
<div>
  
<h3>{{greetMessage}}</h3>

    <ng-content select="app-message"></ng-content>
    </div>

    `
})
export class MessageContainerComponent implements AfterContentInit {
    greetMessage = 'Ignite UI Rocks!';
    @ContentChild(MessageComponent) MessageComponnetContentChild: MessageComponent;
    ngAfterContentInit() {
        console.log(this.MessageComponnetContentChild);
    }
} 

We need to do the following tasks:

  • Import ContnetChild and AfterContnetInit from @angular/core
  • Implement AfterContnetInit life cycle hook to component class
  • Create a variable with decorator @ContnetChild
  • Access that inside ngAfterContnetInit life cycle hook

In the output console you will find a reference of MessageComponent, also if you can notice that __proto__ of MessageComponent is set to Object.

SEE MORE: Angular more popular with Java developers, Python & Node.js developers prefer React

You can modify the ContentChild property inside ngAfterContentInit life cycle hook of the component. Let us assume that there is more than one MessageComponent is projected as shown in the listing below:

import { Component, OnInit } from '@angular/core';
@Component({
    selector: 'app-root',
    template: `
  
<div>
    <app-messagecontainer>
    <app-message *ngFor='let m of messages' [message]='m'></app-message>
    </app-messagecontainer>
  </div>

`
})
export class AppComponent implements OnInit {
    messages: any;
    ngOnInit() {
        this.messages = this.getMessage();
    }
    getMessage() {
        return [
            'Hello India',
            'Which team is winning Super Bowl? ',
            'Have you checked Ignite UI ?',
            'Take your broken heart and make it to the art'
        ];
    }
} 

In the output, you will get many MessgeComponent projected as below:

Now we have more than one ContentChild, so we need to access them as ContentChildren as shown in the listing below:

export class MessageContainerComponent implements AfterContentInit {
    greetMessage = 'Ignite UI Rocks!';
    @ContentChildren(MessageComponent) MessageComponnetContentChild: QueryList<MessageComponent>;
    ngAfterContentInit() {
        console.log(this.MessageComponnetContentChild);
    }
} 

To work with ContentChildren and QueryList, you need to do following tasks:

  • Import ContentChildren , QueryList , AfterContentInit from @angular/core
  • Make reference of ContnetChildren with type QueryList
  • Access ContentChildren reference in ngAfterContentInit() life cycle hook

In the output, you will get various reference of MessageComponent as ContentChildren as shown in the image below:

You can query each item in ContentChildren and modify property as shown in the listing below:

ngAfterContentInit() {
    this.MessageComponnetContentChild.forEach((m) => m.message = 'Foo');
} 

In this way, you can work with ContentChildren in Angular.

SEE MORE: Year in review: Angular in 2017

Summary

ViewChild and ContentChild are two very important features of Angular. It is used to access Child Component in the Parent Component.

Any directive, component, and element which is part of component template is accessed as ViewChild. Whereas, any element or component which is projected inside is accessed as ContentChild.

Like this post?

If you like this post, please share it. In addition, if you haven’t checked out Infragistics Ignite UI for Angular Components, be sure to do so! They’ve got 30+ material based Angular components to help you code speedy web apps faster.

 

This post was originally published on the Infragistics blog.

Author

Dhananjay Kumar

Dhananjay Kumar works as a Developer Evangelist for Infragistics. He is an eight-time Microsoft MVP.

Follow him on twitter @debug_mode for all the updates about his blog posts and workshops.


Leave a Reply

Be the First to Comment!

avatar
400
  Subscribe  
Notify of