Today we will learn about a very important concept called Content Projection
. It's very useful concept and helps to make an application dynamic.
Let's dive in by creating our playground first -
Lets create a component called my-cards
and use it in the app.component.html
file (Hint 😉 using the selector)
Now lets try out a simple exercise. Add the below code in the app.component.html
file -
<app-my-cards>
<span>
This is some content in between the card selector!
</span>
</app-my-cards>
If you open the browser to check the output you will see the text you typed in between the selector i.e. This is some content in between the card selector!
is not visible/ not getting displayed.
So the span element which is the child of the selector is the content
and it's also the child element. So if we combine both the term we get content child
. The span element is the content child.
Now as we saw earlier in the demo the content child is not getting displayed. So in order to display the content child we need to project it. Or in simple term we need to have a special placeholder (in the child component - MyCardsComponent
) which will catch/ receive the value and display it.
This special placeholder is ng-content
.
So now let's update the MyCardsComponent
's template to have-
<ng-content></ng-content>
and now you will see the below output -
So what exactly happens ?
The child content here in this case the span
gets projected in the ng-content
. So ng-content
acts as a placeholder.
Now what if you need multiple placeholders? For example you will pass a content that would sit on the card-header another content as the card-body and another in the card-footer???
For that we need to use something called select
- It is very powerful.
The select can accept a class
, id
, attribute
or an element
. Confused?
Lets see a quick example. Paste in the below code in the app.component.html
file -
<app-my-cards>
<header>Card Header</header>
<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>
And in the my-cards.component.html
file i.e. MyCardsComponent
's template file paste in the below 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>
So the 1️⃣ first ng-content
has a selector which matches an element/ tag - the header tag
2️⃣ Second ng-content
selector matches an id card-sub-header
3️⃣ Third ng-content
selector matches a class card-body
4️⃣ Fourth ng-content
selector matches an attribute title
There can also be a scenario where instead of using header tag (in the above example) you need to use a div tag like below -
<app-my-cards>
<div >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>
Then the header will not work right? And suppose you don't have the option to change the card component also. Then what's the solution ???
ngProjectAs
comes to the rescue!!!
Paste in the below code -
<app-my-cards>
<div 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>
Here you can see the first line is the div
tag. And we have used ngProjectAs='header'
. In this case Angular will consider the selector as header
and the output will work as it is.
That's all for now.
Hope you enjoyed reading the post
If you liked it please like ❤️ share 💞 comment 🧡.
Coming up ContentChild
and ContentChildren
.
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 (5)
Thks for article. Perfect
excelent explanation thanks
excellent article
I am glad you liked it.
What happens when the select matches multiple components?