I have a collection of card objects
...
cards: Card[];
...
ngOnInit() {
this.cardService.getCards.subscribe(r => { this.cards = r; });
}
I add child card components in the template like this
<div id="cards-container">
<app-card *ngFor="let card of cards" [name]="card.name"></app-card>
</div>
The Card component has a name and some style dependant on an active attribute which is toggled by clicking the component
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
'styleUrls: ['./card.component.scss']
})
export class CardComponent {
private _name = '';
private _active = false;
// getters/setters
...
onClick(e) {
this.active = !this.active;
}
}
card.component.html
<div [ngClass]="{'card': true, 'active': _active}"
(click)="onClick($event)">
{{name}}
</div>
All of this works great.
The problem:
In the parent component, I need to iterate through all of the card components which were added with *ngFor="let card of cards"
and set them all active or not active but I cannot figure out how to do this.
What I have tried:
I tried using @ViewChildren()
but the QueryList's toArray()
method always gives me an empty array. From the limited examples in the ViewChildren documentation I am not 100% clear whether I need an additional Directive in the parent component or if the directives in the examples are simply used to demonstrate. So what I tried was
@ViewChildren(CardComponent) cardList: QueryList<CardComponent>;
I also tried using ViewContainerRef using something similar to this answer but I was unable to make it work and it seems like that is not taking me in the right direction. I also looked at the documentation for ComponentRef but I do not see how or if this can help me solve my problem.
Any suggestions to point me in the right direction are appreciated.
UPDATE
My active setter in the card component is like this
@Input()
set active(active: boolean) {
this._active = active;
}
I need to be able to change this for all the cards at any point, in other words, a "select/deselect all" option.
SOLVED!
Taking the suggestion from @Tim Klein, I subscribed to changes
of the QueryList and was able to get my components in an array which I update when the QueryList changes. Now I just iterate the array of components and call my active
setter.
cards: CardComponent[];
@ViewChildren(CardComponent) cardList: QueryList<CardComponent>;
...
ngAfterViewInit(): void {
this.cards = this.cardList.toArray(); // empty array but that's okay
this.cardList.changes.subscribe((r) => {
this.cards = this.cardList.toArray(); // we get all the cards as soon as they're available
});
}