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>
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>
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
};
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;
}
In the devtool when we inspect we can see the below -
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';
}
And you will see the below output -
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>
& in the my-cards.component.ts
file lets add the below code -
<ng-content></ng-content>
You will see the below output.
So the content-projection is working 😊
Now lets create a property and decorate with ContentChild.
@ContentChild(ContentChildDemoComponent)
contentChildDemoProperty: ContentChildDemoComponent | undefined;
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)
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>
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;
And in the ngAfterContentInit
method -
ngAfterContentInit() {
this.contentChildrenDemoProperty
debugger;
}
And if we run the application and debug we will see the below -
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
Top comments (1)
excelllent article