After we mentioned ContentChild
in the last post, we will move our focus onto ContentChildren
, whose only difference is that instead of selecting only one element – it selects a QueryList
of elements from the DOM.
(Wait, what is a QueryList
? QueryList
is an unmodifiable list of elements that Angular keeps track every time the state of the application changes. Both ContentChildren
, and we will later see ViewChildren
use QueryList
to store elements from the DOM and content DOM. QueryList is an array-like data structure, and you guessed it, it has methods such as map
, filter
, find
, forEach
and others.).
When we use ContentChildren
together with QueryList
, any change within the QueryList
is stored. Every time we add, remove or modify an item, the list itself will be updated and the observable bound to the query list will emit a new value. Like last time, we would use the ngAfterContentInit
callback to set our content queries.
@ContentChildren
Let us start by copying the expiry element two more times inside our app.component.html
file. Why would we do that – it could be tedious to create a ContentChild
for every reference we might need. Three might still seem like a low, number but having a directive such as ngFor
, well, let us just say, it could become very, very messy.
<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> <div class="more-data" expiry #expiry> <h3>Expiry Date:</h3> <p>20/05/2024</p> </div> <div class="more-data" expiry #expiry> <h3>Expiry Date:</h3> <p>20/05/2024</p> </div> </app-user-card>
Nothing special, we just copy-pasted the last div
element. Now we will also change the way we reference these elements inside our user-card.component.ts
:
import { AfterContentInit, Component, ContentChildren, ElementRef, OnInit, QueryList, } from '@angular/core'; @Component({ selector: 'app-user-card', templateUrl: './user-card.component.html', styleUrls: ['./user-card.component.css'], }) export class UserCardComponent implements OnInit, AfterContentInit { @ContentChildren('expiry') expiryReferences: QueryList<ElementRef>; constructor() {} ngOnInit() {} ngAfterContentInit() { console.log(this.expiryReferences); } }
We changed the property decorator from ContentChild
to ContentChildren
, and we used QueryList
with the type of ElementRef
in order to create the expiryReference
list. Again, we are using a simple reference of an element, but we also have the freedom to use selectors for @Component
and @Directive
, a token reference or a template reference. The result of the console.log()
can be seen below:
The first thing we notice is that we have three elements in the _results
array. We can quickly access the first and last element using the first
and last
property, the dirty
property tells us whether we changed something in our query list, we have the changes
observable that notifies the user of any changes, and inside the prototype we can find the above-mentioned list of methods that we could use – get
, map
, filter
, find
, forEach
, destroy
and others.
And bam – we get the following result:
Well, printing out the structure of the reference isn’t that interesting. Let us see how to for example, assign a certain background color to every second element. For that, we would need to change the content ngAfterContentInit
to following:
ngAfterContentInit() { let index = 0; for (let reference of this.expiryReferences) { if (index % 2 === 1) { reference.nativeElement.style.backgroundColor = 'blue'; } else { reference.nativeElement.style.backgroundColor = 'red'; } index++; } }
We are just iterating through the list of references we have and check whether the number is divisible by two. In one case we are just setting our background color to blue, otherwise to red. This would work not only for three elements, but for 10, 100, or any other number with a big amount of zeroes :).
For the complete project, please look at the Stackblitz below:
Final Words
So, when to use ContentChildren
. Simply, if we see the need for ContentChild
(projecting content), but at the same time realize that we might need to have multiple elements AND we might need a data structure to keep track of them. In that case, look no further than ContentChildren
.
For more articles please click below, or check the blog.