'Clickable table cells in angular

I have one table with rows. When I click on a cell it has changed color to red but only one at a time can be red. If I click on another cell, the previous cell should no longer be red. I don't know how to make only one cell red at a time. Do you have any ideas?

html file:

<table>
  <tr appFocusCell><td > </td></tr>
  <tr appFocusCell><td> </td></tr>
  <tr appFocusCell><td> </td></tr>
  <tr appFocusCell><td> </td></tr>
  <tr appFocusCell><td> </td></tr>
  <tr appFocusCell><td> </td></tr>
  <tr appFocusCell><td></td></tr>
</table>

focus-cell.directive file:

import {Directive, ElementRef, HostListener, Renderer2} from '@angular/core';

@Directive({
  selector: '[appFocusCell]'
})
export class FocusCellDirective {

  constructor(private e:ElementRef,private render: Renderer2) { }
     ngOnInit() {

      }

      @HostListener('click')
  onClick(){
    const div = this.e.nativeElement;
    this.render.setStyle(div,'background','red')
      }

}


Solution 1:[1]

Following is the code sample

html

<table border="1">
  <tbody>
    <tr>
      <td appCellClick>
        Cell1.1
      </td>
      <td appCellClick>
        Cell1.2
      </td>
    </tr>
    <tr>
      <td appCellClick>
        Cell2.1
      </td>
      <td appCellClick>
        Cell2.2
      </td>
    </tr>
  </tbody>
</table>

directive

import { Directive, HostListener, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appCellClick]',
})
export class CellClickDirective {
  
  @HostListener('click', ['$event']) onClick(ev) {
    
    const curEl = ev.target; //TAKE CLICKED CELL ELEMENT
    
    let parentEl:any = {};

    // CHECK THE PARENT TABLE ELEMENT
    const parentChecker = (el) => {
      if (el.nodeName == 'TABLE') {
        parentEl = el;
      } else {
        parentChecker(el.parentElement);
      }
    };
    
    parentChecker(curEl);
    // PARENT TABLE FOUND - REMOVE EXISTING CSS-CLASS
    if(parentEl){
      const addedBg = parentEl.querySelectorAll('td.bg-red');
      addedBg.forEach((e) => {
        e.setAttribute('class', e.getAttribute('class').replace('bg-red',''));
      });
    }    

    this.renderer.addClass(curEl, 'bg-red');

  }

  constructor(
    private renderer: Renderer2
  ) {}
}

css

.bg-red{
  background-color: red;
}

Solution 2:[2]

You might want to add logic about which row is currently selected, by means of creating a parent directive to bookmark the currently clicked row.

The idea is, with information about which row is currently clicked, this parent directive can remove highlight of the old row clicked and highlight the newly clicked row instead.

Here's a sample solution. I have not tested it, but hopefully the idea behind it is clear.

<table appTable> <!-- ? make new directive-->
  <tr appFocusCell>...</tr>
  <tr appFocusCell>...</tr>
  ...
</table>
@Directive({
  selector: '[appFocusCell]'
})
export class FocusCellDirective implements OnDestroy {
  readonly onClick$ = new Subject<void>(); // ? emit when host is clicked

  @HostListener('click')
  onClick() {
    this.onClick$.next();
  }

  // ? set/unset row background color whether active/inactive
  set isActive(isActive: boolean) {
    this.e.style.backgroundColor = isActive ? 'red' : 'unset';
  }

  constructor(private e: ElementRef<HTMLElement>) { }

  ngOnDestroy() {
    this.onClick$.complete();
  }
}
@Directive({
  selector: '[appTable]'
})
export class AppTableDirective implements AfterContentInit, OnDestroy {
  private clickedRow: FocusCellDirective | null = null; // ? bookmark currently clicked row

  @ContentChildren(FocusCellDirective, { descendants: true })
  private rows!: QueryList<FocusCellDirective>; // ? get all table rows

  private onDestroy = new Subject<void>();

  ngAfterContentInit() {
    // ? listen to any rows clicked
    this.rows.forEach((row) => {
      row.onClick$
        .pipe(takeUntil(this.onDestroy))
        .subscribe(() => this.setClicked(row)); // ? update currently clicked row
    });
  }

  private setClicked(row: FocusCellDirective) {
    if(this.clickedRow !== null) {
      this.clickedRow.isActive = false; // ? remove highlight of previously clicked row
    }

    row.isActive = true; // ? highlight currently clicked row

    this.clickedRow = row; // ? bookmark currently clicked row
  }

  ngOnDestroy() {
    this.onDestroy.next();
    this.onDestroy.complete();
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nathash Kumar
Solution 2