Reputation: 1016
I have the following template :
<app-subscriber
*ngFor="let stream of streams"
[stream]="stream"
[session]="session"
(speakEvents)='onSpeakEvent($event)'>
</app-subscriber>
The parent component listens for events from speakEvents
as so :
onSpeakEvent(event: any) {
if (event.type === 'speaking') {
this.spotlightSubscriber = event.emitter;
//The component emitting the event, from the ngFor repeat list.
//Add class to this.spotlightSubscriber
}
else if (event.type === 'stopped_speaking') {
//Remove class to this.spotlightSubscriber
}
}
How can I dynamically add/remove a CSS class to the event emitter, it being in a *ngFor
loop?
I need to add the class to one element at a time.
Upvotes: 4
Views: 7043
Reputation: 39432
There are basically two ways of achieving what you're trying to achieve here.
Create a property(indexToAppendClassTo
) to track the indexes which you want to apply the class on. When the event is emitted by the Child Component, add or remove the index of the emitted stream from this indexToAppendClassTo
list. And apply the class based on the presence of the index of a stream in this indexToAppendClassTo
list.
In Class:
import { Component } from '@angular/core';
@Component({...})
export class AppComponent {
...
indexToAppendClassTo: any[] = [];
onSpeakEvent(event: any, index) {
// Approach 1
if (event.type === 'speaking') {
this.spotlightSubscriber = event.emitter;
if(this.indexToAppendClassTo.indexOf(index) === -1)
this.indexToAppendClassTo.push(index);
} else if (event.type === 'stopped_speaking') {
if(this.indexToAppendClassTo.indexOf(index) > -1)
this.indexToAppendClassTo.splice(index, 1);
}
}
}
And in the template:
<app-subscriber
*ngFor="let stream of streams; let i = index;"
[stream]="stream"
[session]="session"
(speakEvents)='onSpeakEvent($event, i)'
[ngClass]="indexToAppendClassTo.includes(i) ? 'color' : ''">
</app-subscriber>
As mentioned by others, if you're willing to send some property as a part of the emitted event that decides whether or not to apply the class, then do that from your Child Component and then pass the updated stream
from the Child as the emitted $event
data. That way you won't have to unnecessarily manage the indexToAppendClassTo
list:
In Parent Component Class:
import { Component } from '@angular/core';
@Component({...})
export class AppComponent {
...
onSpeakEvent(event: any) {
// Approach 2
const indexOfElement = this.streams.findIndex(strem => strem.name === event.name);
this.streams[indexOfElement] = { ...event };
}
}
In Parent Component Template:
<app-subscriber
*ngFor="let stream of streams"
[stream]="stream"
[session]="session"
(speakEvents)='onSpeakEvent($event)'
[ngClass]="stream.type === 'speaking' ? 'color': ''">
</app-subscriber>
And In Child Component Class:
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-subscriber',
templateUrl: './subscriber.component.html',
styleUrls: ['./subscriber.component.css']
})
export class SubscriberComponent implements OnInit {
@Input() stream;
@Input() session;
@Output() speakEvents: EventEmitter<any> = new EventEmitter<any>();
...
onClick() {
this.speakEvents.emit({ ...this.stream, type: 'type of the stream here' });
}
...
}
And in Child Component Template:
<button (click)="onClick()">{{stream.name}}</button>
Here's a Working Sample StackBlitz with both the appraoches for your ref.
Upvotes: 3