DEV Community

Anubhab Mukherjee for This is Angular

Posted on

ContentChild and ContentChildren in Angular

Today we will learn about ContentChild and ContentChildren in Angular.

On a high level ContentChild and ContentChildren are property decorators. They are used to query or helps to get a reference to the projected content. If you are not aware of Content Projection I would highly recommend you to go through this post.

Lets see our playground first.
We have a MyCardsComponent component where few items are projected from the parent component (AppComponent).
The parent Component Template Code-

<app-my-cards>
    <div #header ngProjectAs='header'>Card Header</div>
    <span id='card-sub-header'>Card Sub Header</span>
    <div class="card-body">
        This is a card Body!!!
    </div>
    <footer title="card-footer">
        Card Footer.
    </footer>
</app-my-cards>
Enter fullscreen mode Exit fullscreen mode

The Child Component Code-

<ng-content select='header'></ng-content>
<ng-content select='#card-sub-header'></ng-content>
<ng-content select='.card-body'></ng-content>
<ng-content select='[title]'></ng-content>
Enter fullscreen mode Exit fullscreen mode

And the output till now -
Image description

Here we will try to get the reference of the projected content in the Child Component to do some manipulation (say adding some style). In this scenario ViewChild won't be helpful as it will not work. For this we need a new decorator called ContentChild/ ContentChildren decorator.

Lets paste in the below code in the MyCardsComponent-

  @ContentChild('header')
  cardHeaderData: ElementRef = {
    nativeElement: undefined
  };
Enter fullscreen mode Exit fullscreen mode

So here in the above code we are defining a property cardHeaderData and decorating with ContentChild
cardHeaderData is of type ElementRef (A wrapper around the native element inside of a View)
Now the next question can come - okay we can access the element but where we can get hold of the element for the first time and how to prove that we got hold of the element?
For this there is another lifecycle hook provided by Angular - the ngContentInit().
This method is called once the projected content is initialized.

Note
Projected content will be accessible for the first time in the ngAfterContentInit lifecycle hook method.

So lets implement the function and see how it looks like. Paste in the below code -

  ngAfterContentInit() {
    this.cardHeaderData
    debugger;
  }
Enter fullscreen mode Exit fullscreen mode

In the devtool when we inspect we can see the below -
Image description
Here 👆🏻 we can see that the reference of the element (which was projected) we can get using the ContentChild decorator and its a native element.
Once we get hold of the element we can do manipulation like adding a style programmatically and many more cool things. To change the style lets add the below code -

  ngAfterContentInit() {
    this.cardHeaderData.nativeElement.style.color = 'blue';
    this.cardHeaderData.nativeElement.style.backgroundColor = 
'yellow';
    this.cardHeaderData.nativeElement.style.fontSize = '24px';
  }
Enter fullscreen mode Exit fullscreen mode

And you will see the below output -
Image description
So using the property we can target the nativeElement and set the color and do all the DOM tricks.

Here we are targeting a HTML element (like div), but lets see what if we project a Component how to access that.

So lets create a component ContentChildDemo. You should be a ninja by now to create a component using CLI & even if you are 1 step away from becoming a ninja you can follow this post.
And use the selector in app.component.html file like below -

<app-my-cards>
 <app-content-child-demo></app-content-child-demo>
</app-my-cards>
Enter fullscreen mode Exit fullscreen mode

& in the my-cards.component.ts file lets add the below code -

<ng-content></ng-content>
Enter fullscreen mode Exit fullscreen mode

You will see the below output.
Image description
So the content-projection is working 😊
Now lets create a property and decorate with ContentChild.

  @ContentChild(ContentChildDemoComponent)
  contentChildDemoProperty: ContentChildDemoComponent | undefined;
Enter fullscreen mode Exit fullscreen mode

Here above you can see the ContentChildDecorator is accepting the name of the component you are trying to reference (In this case ContentChildDemoComponent), but in the first demo we were passing the reference (header)
Image description
Note:
1️⃣ When accessing Component we just pass the name of the component.
2️⃣ When accessing a projected component, using the property you can even call a method present inside that projected content component.
3️⃣ When accessing a native element we need to add a reference and pass the same reference to the ContentChild

Now lets understand when the ContentChildren comes into play.
Say in the above example we are projecting (passing from the parent) only 1 ContentChildDemoComponent. But what if a scenario arises where you are passing multiple components and you need to access them?

Something like below -

<app-my-cards>
 <app-content-child-demo></app-content-child-demo>
 <app-content-child-demo></app-content-child-demo>
 <app-content-child-demo></app-content-child-demo>
</app-my-cards>
Enter fullscreen mode Exit fullscreen mode

In the above case ContentChild will return only the first match (very important remember this point).
If you want to get hold of all the components projected you need to use the ContentChildren decorator.

Lets add a new property like below -

  @ContentChildren(ContentChildDemoComponent)
  contentChildrenDemoProperty: 
   QueryList<ContentChildDemoComponent> | undefined;
Enter fullscreen mode Exit fullscreen mode

And in the ngAfterContentInit method -

  ngAfterContentInit() {
    this.contentChildrenDemoProperty
    debugger;
  }
Enter fullscreen mode Exit fullscreen mode

And if we run the application and debug we will see the below -
Image description
Here above we can see a QueryList (An unmodifiable list) is being returned. You can loop through and access every item. Same powerful heavy lifting you can do on all the matching items in the QueryList.

That's all for now.

Hope you enjoyed reading the post

If you liked it please like ❤️ share 💞 comment 🧡.

Coming up ChangeDetection
So stay tuned.

I will be tweeting more on Angular JavaScript TypeScript CSS tips and tricks.

So hope to see you there too 😃

Cheers 🍻
Happy Coding

Discussion (1)

Collapse
oliverdjbrown profile image
Oliver Brown

excelllent article