I'm trying to create a carousel component that has n amount of child components based on whatever size list it will get. I want the parent (carousel) to control the styling for children as it manipulates the size of the list and track the indexes to rotate items around the carousel and style accordingly. There will buttons to control this behaviour.
I'm trying to apply styles and get the functionality working but cannot seem to access the styles by getting a query list of element refs using view children and template refs (I also tried with content children initially, with the template set up properly). I can change them if I inject the element ref and renderer at the child level, but I don't want to do this here.
I get the error:
TypeError: Cannot read properties of undefined (reading 'style')
But not when I do the exact same thing doing it from the child level.
Am I going about this incorrectly? How do I change children styles in a list from @ViewChildren() from a parent?
Parent component (carousel.component.html)
<div class="caro-body">
<h3 class="caro-body__title">Carousel Title</h3>
<div class="caro-body__items">
<app-upcoming-card
#caroItem
*ngFor="let card of list; let i = index"
></app-upcoming-card>
</div>
</div>
(carousel.component.ts)
import {
Component,
OnInit,
AfterViewInit,
Renderer2,
ViewChildren,
QueryList,
ElementRef,
} from '@angular/core';
import { UpcomingCardComponent } from '../upcoming-card/upcoming-card.component';
@Component({
selector: 'app-carousel',
templateUrl: './carousel.component.html',
styleUrls: ['./carousel.component.scss'],
})
export class CarouselComponent implements OnInit, AfterViewInit {
@ViewChildren('caroItem') caroItems: QueryList<ElementRef<UpcomingCardComponent>>
list = [
{ billName: 'Rent', billAmount: 850, billDate: Date.now() },
{ billName: 'Groceries', billAmount: 250, billDate: Date.now() },
{ billName: 'Internet', billAmount: 80, billDate: Date.now() },
{ billName: 'Phone', billAmount: 45, billDate: Date.now() },
{ billName: 'Loan', billAmount: 50, billDate: Date.now() },
{ billName: 'Transit', billAmount: 20, billDate: Date.now() },
{ billName: 'Dining Out', billAmount: 50, billDate: Date.now() },
];
constructor(private renderer: Renderer2) {}
ngOnInit(): void {}
ngAfterViewInit() {
this.caroItems.forEach(item => {
this.renderer.setStyle(item.nativeElement, 'background-color', 'red')
})
}
}
Child component (upcoming-card.component.html)
<div class="card">
<div class="card__title">{{ card.billName }}</div>
<div class="card__amount">{{ card.billAmount | currency }}</div>
<div class="card__date">{{ card.billDate | date }}</div>
</div>
(upcoming-card.component.ts)
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-upcoming-card',
templateUrl: './upcoming-card.component.html',
styleUrls: ['./upcoming-card.component.scss']
})
export class UpcomingCardComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
I've tried accessing the list using @ContentChildren, having the template structure properly for this case as well as using the proper lifecycle hook, but it results in the same. I can update styles by injecting the element ref and renderer into the child, but I want to do it at the parent level.
Thanks to a small modification of your code, I was eventually able to access the nativeElement of the ViewChildren. I passed the argument
{read: ElementRef}
to@ViewChildren
and from then onitem.nativeElement
was no longer undefined.Please see my code-snippet below or check out my working example on stackblitz where I accessed two nav-bars via @ViewChildren and colored them red.