angulartypescriptangular-ng-class

add ngClass to all the divs clicked


enter image description here

I'm trying to add 'active' class to all the divs (options in this case) that are clicked and remove the class on a second click.

The options count can vary as it's generated by ngFor.

HTML

<div [ngClass]="{'active': selectedOptions == i}" *ngFor="let opt of options; let i=index"
 (click)="answerSelectedMultiple(i)">

TS

answerSelectedMultiple(index){
    if(this.selectedOptions.indexOf(index) == -1){
        this.selectedOptions.push(index);
    }else{
        this.selectedOptions.splice(this.selectedOptions.indexOf(index), 1);
    }
}

Solution

  • You could capsulate this into a directive:

    @Directive({
      selector: '[activeOnClick]',
      // Optionally replace this both with properties marked 
      // with @HostBinding and @HostListener decorators
      host: {
        '[class.active]': 'clicked',
        '(click)': '_toggle()'
      }
    })
    export class ActiveOnClickDirective {
       @Input('activeOnClick')
       clicked = false;
    
       _toggle(){
          this.clicked= !this.clicked;
       }
    }
    

    After declaring the directive in a module, it could be used as follows:

    <div activeOnClick *ngFor="let opt of options; let i=index">
    

    In the case that you want to bind the flag value of the directive in the template:

    <div [activeOnClick]="true" *ngFor="let opt of options; let i=index">
    

    You could even expand this to dynamically apply different classes on the host element:

    @Directive({
      selector: '[classesOnClick]'
    })
    export class ClassesOnClickDirective { 
       @Input('classesOnClick')
       clicked = false;
    
       @Input()
       set class(value: string[]| string){
          let classes = Array.isArray(value) ? value : value.split(' ');
          classes = classes.filter(class => !!class);
          this._classes = classes;
       }
       private _classes: string [] = ['active'];
    
       constructor(private element: ElementRef, private renderer: Renderer2){}
    
       @HostListener('click')
       _toggle(){
          this.clicked = !this.clicked;
       }
    
       private _update(){
         this._classes.forEach(class => {
           if(this.clicked){
              this.renderer.addClass(this.element.nativeElement, class);
           }else{
              this.renderer.removeClass(this.element.nativeElement, class);
           } 
         });
       }
    }
    

    Then use as follows:

    <div [classesOnClick]="true" [classesOnClickClass]="['c1','c2']" *ngFor="let opt of options; let i=index">
    
    <div [classesOnClick]="true" classesOnClickClass="c1 c2" *ngFor="let opt of options; let i=index">