Assign a dynamic class to an element within an ngFor iteration

I am working with a template that includes an app-subscriber component being iterated over using *ngFor:

<app-subscriber 
  *ngFor="let stream of streams" 
  [stream]="stream" 
  [session]="session" 
  (speakEvents)='onSpeakEvent($event)'>
</app-subscriber>

The parent component is listening for events triggered by the speakEvents emitter, and handles them like this:

onSpeakEvent(event: any) {
    if (event.type === 'speaking') {
      this.spotlightSubscriber = event.emitter; 
      //Assigning the speaking element to spotlightSubscriber
      //Add class to this.spotlightSubscriber
    }
    else if (event.type === 'stopped_speaking') {
      //Remove class from this.spotlightSubscriber
    }
  }

I'm trying to figure out how to dynamically add or remove a CSS class from the event emitter within the ngFor loop. The goal is to apply the class to one element at a time.

Answer №1

There are essentially two methods to achieve the desired outcome here.

Method 1:

To track the indexes where you want to apply a class, create a property (indexToAppendClassTo). When the Child Component emits an event, add or remove the index of the emitted stream from this indexToAppendClassTo list. Apply the class based on the presence of the index in this list.

In Class:

import { Component } from '@angular/core';

@Component({...})
export class AppComponent {
  ...
  indexToAppendClassTo: any[] = [];

  onSpeakEvent(event: any, index) {
    // Method 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>

Method 2

If you're open to sending a property as part of the emitted event to determine whether to apply the class, do that from your Child Component and pass the updated stream as the emitted data. This way, you won't need to manage the indexToAppendClassTo list:

In Parent Component Class:

import { Component } from '@angular/core';

@Component({...})
export class AppComponent {
  ...

  onSpeakEvent(event: any) {
    // Method 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 approaches for reference.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Enhancing the 'UserDetails' model in ASP.NET Angular project (dotnet core 3.0) to accommodate additional user information

Currently working on an ASP.NET Web application with Angular as the front end, using the new template in VisualStudio 2019 for ASP.NET angular with Individual Authentication. This project runs on dotnet core 3.0 Preview 4. https://i.stack.imgur.com/RUKdg. ...

What are the best strategies to troubleshoot issues during NPM Install?

I keep encountering errors during the npm install process, but everything works fine when I use npm install --force in my local environment. However, the issues persist during the repository build as my .yaml file script contains "npm install". Can anyone ...

Is it possible to implement websockets with inversify-express-utils?

We are attempting to integrate websockets into our typescript application built on inversify-express-utils, but so far we have had no success: import 'reflect-metadata'; import {interfaces, InversifyExpressServer, TYPE} from 'inversify-expr ...

Tips for making a oneOf field nullable using TypeScript and AJV

A field named platform exists in my code, and it can hold either a string or an array of strings (string[]). The field can also be nullable or undefined if not passed as input. TypeScript Interface export interface IEntityLeaderboardQuery { rank: stri ...

Apply a CSS class when the tab key is pressed by the user

Currently in my Angular 14 project, I am working on a feature where I need to apply "display: block" to an element once the user reaches it using the tab key. However, I am struggling with removing the "display: block" when the user tabs out of the element ...

While using Angular CLI on GitLab CI, an error occurred indicating that the custom rule directory "codelyzer" could not be

ng lint is throwing an error on Gitlab CI stating: An unhandled exception occurred: Failed to load /builds/trade-up/trade-up/common/projects/trade-up-common/tslint.json: Could not find custom rule directory: codelyzer. The strange thing is that ng lint ru ...

What is the process for importing a map from an external JSON file?

I have a JSON file with the following configuration data: { "config1": { //this is like a map "a": [ "string1", "string2"], "b": [ "string1", "string2"] } } Previously, before transitioning to TypeScript, the code below worked: import ...

developing a collection of Material UI text fields

My goal is to construct an accordion containing several textfield mui-components. I have developed a unique render function incorporating all the essential tags and syntax for creating a text field component. Now, I am looking to generate an array of text ...

Show real-time validation messages as the form control values are updated

Instructions: Visit Plunker Locate the input box labeled 'Name' Do not enter anything in the 'Name' field Move to the 'Email' field and start typing An error message will appear for the 'Name' field as you type in ...

Error in Typescript index: iterating over properties of a typed object

My scenario involves an interface that extends multiple other interfaces from an external library: interface LabeledProps extends TextProps, ComponentProps { id: string; count: number; ... } In a different section of the codebase, there is an ...

Using Node.js with Typescript and RedisJSON allows for a powerful and efficient

I've recently started delving into nodejs, typescript, and redis for programming. However, I've encountered an issue with redis: when defining a data interface to be stored in redis, the system throws errors as soon as I try to specify the data t ...

Navigating nested data structures in reactive forms

When performing a POST request, we often create something similar to: const userData = this.userForm.value; Imagine you have the following template: <input type="text" id="userName" formControlName="userName"> <input type="email" id="userEmail" ...

Is there a way to fetch a particular object from Firebase database based on its value using AngularFire2?

Here is the database I am working with: firebase database I am trying to retrieve a dish that has its 'featured' attribute set to true (dish.feature = true). Is it possible to do this directly from the database, or do I have to retrieve all di ...

Impact when returning a React.FC Component

Using React, I have encountered a challenge with my site: I have a function that generates a Card component displaying information about my store's products (#1). To display this on the screen, I map through the array returned by the backend and pass ...

Encountering Build Issue: "NgSemanticModule is not recognized as an NgModule" persists despite inclusion of dependencies and importing into primary module

I have posted my module, component, and package file here. I am attempting to implement a click event with ngif, but I keep encountering an error. The specific error message is "ERROR in NgSemanticModule is not an NgModule". I'm unsure if this error ...

Enabling Angular Elements to handle non-string properties and inputs

When working with Angular Elements, inputs can be supplied through HTML attributes like so: <some-custom-element someArg="test value"><some-custom-element> An alternative method is utilizing setAttribute. However, it's important to note ...

What sets template-driven and reactive forms apart in practice?

Exploring the Angular2 new Forms API has revealed two distinct approaches to forms: Template driven and reactive (model-driven) forms. I am curious about the real-world differences between these two methods, beyond just syntax. Which approach is more adva ...

How can you store form field validation rules (for example, firstname.dirty) in an array within TypeScript in Angular?

How do I save form field validation rules in an array? What should replace /'''''HERE'''''/ with? formfields: Array<Object> = [ {label: "Employer:", control: "employer", val ...

When validating storage content, session value appears as null

I have been working on developing an Ionic app that requires creating a session for user login. The goal is to store the user's name upon logging in, so it can be checked later if the user is still logged in. I have created a model class and a user cl ...

Creating a custom Higher Order Component to seamlessly connect react-relay and react-router using TypeScript

Hey there! So, my Frankenstein monster project has decided to go rogue and I'm running out of hair to pull out. Any help would be greatly appreciated. I've been working on setting up a simple app with React, React-Router, React-Relay, and Typesc ...