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

Angular's Dynamic Injection: Introducing a new component into its parent component

I am looking for guidance on injecting a component dynamically into another in Angular 4, as well as passing values from the parent component to the child component. If anyone can provide a sample of working code, it would be greatly appreciated. ...

Incorrectly calling Asset URL in a single spa micro front-end

Currently, I am utilizing the single-spa library to implement micro front-end functionality. My challenge lies in using assets within my pages as the generated URLs for fetching these assets are incorrect. For example, when attempting to use an informati ...

Angular configuration files fail to be exchanged promptly

I am utilizing Angular 9 with the View Engine compiler. I have two separate files where I hold environment variables: environment.ts: export const environment: any = { production: false, }; environment.prod.ts: export const environment: any = { ...

Angular - Strategies for Handling Observables within a Component

I am new to Angular and recently started learning how to manage state centrally using ngRx. However, I am facing a challenge as I have never used an Observable before. In my code, I have a cart that holds an array of objects. My goal is to iterate over the ...

Customizing the Context Menu in ag-Grid to Trigger Modal Dialog Opening

I encountered a coding problem while trying to customize the menu in Angular ag-grid. My goal is to enable menu customizations like view details, edit data, and delete when right-clicking on a cell. The issue arises when I click "edit" and the component di ...

The attribute 'data' is not found in the type 'IntrinsicAttributes & IProps'. Error code: ts(2322)

I encountered the following issue: Error: Type '{ data: never; }' is not compatible with type 'IntrinsicAttributes & IProps'. The property 'data' does not exist on the type 'IntrinsicAttributes & IProps'. import { ...

Exploring the use of global variables in React

Welcome to my React learning journey! I've encountered an issue while trying to access a global variable exposed by a browser extension that I'm using. Initially, I attempted to check for the availability of the variable in the componentDidMount ...

How can you connect one data field to another within Angular?

Angular offers a convenient method for binding the value of an HTML element to a data field. For example, you can achieve this with the following code: <input name="firstName" [(ngModel)]="firstName"/> This means that any text en ...

What is the best way to search through an array in TypeORM?

I am working on implementing user permissions management using TypeORM with PostgreSQL. The permissions are defined within the user entity in the following column: @Column({ type: 'text', array: true }) permissions: UserPermission[] = []; Th ...

Angular Universal: Dividing projects [A resolution for dated or intricate projects]

Working on an aging Angular project that has been through multiple upgrades since the 2.0 release, we are currently at version 4.3. The project has become quite complex over time, with numerous features and outdated peer dependencies that are no longer be ...

Expanding the capabilities of generic functions in TypeScript using type variables

When working with generics in TypeScript, it is often beneficial for a function that accepts generically-typed arguments to have knowledge of the type. There exists a standard approach to achieve this using named functions: interface TestInterface<T> ...

Router-outlet @input decorator experiencing issues with multiple components

Within my router module, I have a route set up for /dashboard. This route includes multiple components: a parent component and several child components. The data I am trying to pass (an array of objects called tablesPanorama) is being sent from the parent ...

Converting an empty response to JSON in Angular2's Http module

Hello, I am currently working with the following code snippet: save<T>(url: string, data:any, headers = null): Observable<T>{ return this.http .post(url, data, headers) .map(x =>{ return x.json() as T ...

How to efficiently upload multiple files simultaneously in Angular 10 and .NET Core 5 by utilizing a JSON object

I have a JSON object structured like this: Class->Students this is a basic representation of my TypeScript class export class Classroom { Id:number; Name:string; Students:Student[]=[]; } export class Student { Name:string; Age:number; Sex:string; Imag ...

Angular's table data display feature is unfortunately lacking

Below is a simple HTML code snippet: <div class="dialogs"> <div id="wrapper" > <p>{{createTestingConstant()}}</p> <ng-container *ngFor="let one of contacts"> <p>{{one ...

Creating a TypeScript definition file to allow instantiation of a module

I'm encountering an issue with creating a declaration file for an existing module. When using JavaScript, the module is imported using the following syntax: var Library = require('thirdpartylibs'); var libInstance = new Library(); I have ...

Transforming Typescript Strings into ##,## Format

As I've been using a method to convert strings into ##,## format, I can't help but wonder if there's an easier way to achieve the same result. Any suggestions? return new Intl.NumberFormat('de-DE', { minimumFractionDigits: 2, max ...

Using Angular2 to assign the response from an http.get request to a class object

I am a beginner in Angular and I have a JSON file that holds the configuration URL for my application. Path: app/config/development.json { "apiUrl": "http://staging.domain.com:9000/", "debugging": true } Below is the content of my config.service.t ...

Encountering difficulty adjusting the size of my charts when attempting to utilize the billboard.js chart library with Angular 6

I am currently working on integrating the billboard.js chart library with Angular 6, but I am encountering an issue with the size of the chart. Upon the initial page load, the chart appears larger than its containing div element. However, when I resize the ...

What are some solutions to the "t provider not found" error?

Upon deploying my application on the production server using GitLab/Docker, I encountered the following error message: ERROR Error: No provider for t! at C (vendor.32b03a44e7dc21762830.bundle.js:1) at k (vendor.32b03a44e7dc21762830.bundle.js:1) at t._thr ...