ContentChild
is a property decorator in Angular that configures a content query or retrieves a reference to a projected content. We can use ContentChild
in order to retrieve the first element (template reference, component, or directive) matching the selector from the DOM. We can define the component/directive for querying using the selector metadata property, and we can choose any class with @Component
or @Directive
decorator, a template reference (#component
), a provider defined through a string token or a template using TemplateRef
.
If this all seems a bit confusing, the best way to learn is using a practical example, so let us start with writing some code!
@ContentChild
We will use the same Card project playground we used last time. If you remember the code in our template, we projected two contents with selectors fav-day
and dislikes
. What if we would want to get the reference of projected content in order to do some direct changes to it? Well, we can use the above-mentioned @ContentChild
decorator. First, let us create a new projected content using a template reference this time. This is our updated app.component.html
file.
<app-user-card> <div class="more-data" fav-day> <h3>Favorite day:</h3> <p>Sunday</p> </div> <div class="more-data" dislikes> <h3>Dislikes:</h3> <p>The alarm clock</p> </div> <div class="more-data" expiry #expiry> <h3>Expiry Date:</h3> <p>20/05/2024</p> </div> </app-user-card>
And this is our new user-card.component.html
file:
<div class="wrapper"> <div class="basic-info"> <div class="image"> <img src="https://avatars.dicebear.com/api/male/tom.svg?background=%230000ff" /> </div> <div class="personal-data"> <h3>Name:</h3> <p>Tom</p> <h3>Surname:</h3> <p>Doe</p> </div> </div> <ng-content select="[fav-day]"></ng-content> <div class="more-data"> <h3>Likes:</h3> <p>Football, swimming, beer</p> </div> <ng-content select="[dislikes]"></ng-content> <ng-content select="[expiry]"></ng-content> </div>
Let us notice two new things above. We used the template reference (#expiry
) with the hash symbol in order to declare content that we will grab. Template references are used in order to be more detailed when working with projected data. This just means that we declared a new variable, expiry
on our <div>
element, which we will utilize later on. Everything else is more or less the same:
In order to use this projected content, we need to change a couple of things in the logic of our component. This is our updated user-card.component.ts
file:
import { AfterContentInit, Component, ContentChild, ElementRef, OnInit, } from '@angular/core'; @Component({ selector: 'app-user-card', templateUrl: './user-card.component.html', styleUrls: ['./user-card.component.css'], }) export class UserCardComponent implements OnInit, AfterContentInit { @ContentChild('expiry') expiryReference: ElementRef; constructor() {} ngOnInit() {} ngAfterContentInit() { console.log(this.expiryReference); } }
If we check our console we will see that the console.log(this.expiryReference);
now returns the reference to our element which is amazing. But before we start manipulating our projected element let us explain these new concepts that we see here.
Let us start from the AfterContentInit
lifecycle hook. It is called when the component’s external content has been attached, or when we project content, using <ng-content>
tags. To put it simply we need to write our code regarding our projected content inside our method (you can test it by writing the console.log
directive inside ngOnInit
and seeing what you get back).
Then we have ElementRef
which is a wrapper around a native DOM element. Using ElementRef we can access the underlying element and its DOM API without any issues.
Now when we have the element we can dynamically add or remove styles, or do any other number of programmatic manipulations. For example, we can change the style of our element, by targeting the nativeElement
and setting background colors, borders, padding, margin, font sizes and other properties:
import { AfterContentInit, Component, ContentChild, ElementRef, OnInit, } from '@angular/core'; @Component({ selector: 'app-user-card', templateUrl: './user-card.component.html', styleUrls: ['./user-card.component.css'], }) export class UserCardComponent implements OnInit, AfterContentInit { @ContentChild('expiry') expiryReference: ElementRef; constructor() {} ngOnInit() {} ngAfterContentInit() { this.expiryReference.nativeElement.style.backgroundColor = 'salmon'; this.expiryReference.nativeElement.style.padding = '10px'; this.expiryReference.nativeElement.style.borderRadius = '10px'; } }
With the following result:
For the complete project, please look at the Stackblitz below:
Final Words
Well, this is a fairly simplistic overview of ContentChild
. The truth is that the decorator is a lot more powerful because we can query whole directives or components inside our <ng-content></ng-content>
. There is a caveat though, ContentChild
only targets the first child component, in order to access/update multiple elements we would use ContentChildren
which we will mention in the next post.
For more articles please click below, or check the blog.
Pingback: Angular ContentChildren -
Pingback: Angular ViewChild -
Pingback: Angular ViewChildren -
Comments are closed.