How to add/remove class from directive
Asked Answered
L

4

22

Using a custom directive how would you add/remove a class on the host element based on a specific conditions?

Example:

@Directive({
  selector: '[customDirective]'
})
export class CustomDirective {
  constructor(service: SomService) {
    // code to add class

    if (service.someCondition()) {
        // code to remove class
    }
  }
}
Leban answered 7/1, 2017 at 10:58 Comment(1)
I think you know how to add styles with host binding but classes are not supported inside directives I guess :/ #35915933Berceuse
P
36

If you don't want to use the ngClass directive (Hint: you can pass a function to [ngClass]="myClasses()" if it would be to messy inline in your template) you can just utilize the Renderer2 for it to add one or more classes:

export class CustomDirective {

   constructor(private renderer: Renderer2,
               private elementRef: ElementRef,
               service: SomService) {
   }

   addClass(className: string, element: any) {
       this.renderer.addClass(element, className);
       // or use the host element directly
       // this.renderer.addClass(this.elementRef.nativeElement, className);
   }

   removeClass(className: string, element: any) {
       this.renderer.removeClass(element, className);
   }

}
Ptolemaic answered 7/1, 2017 at 12:24 Comment(2)
What is host element?Terle
@Terle the element on which the directive is attached toPtolemaic
M
17

When you are using directives in Angular you would want to use @HostBinding, and bind to class.your-class in order to be able to add/remove your class based on a predicate. You don't need to DI in the Renderer2 to effectively add/remove classes.

For example, when using Bootstrap and Reactive Forms and you want to indicate a valid or invalid form field you can do something like:

import { Directive, Self, HostBinding, Input } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
  selector: '[appCheckFormFieldValidity]'
})
export class CheckFormFieldValidity{
  @Input() public class: string;

  constructor(
    @Self() private ngControl: NgControl
  ) { }

  @HostBinding('class.is-valid')
  public get isValid(): boolean {
    return this.valid;
  }

  @HostBinding('class.is-invalid')
  public get isInvalid(): boolean {
    return this.invalid;
  }

  public get valid(): boolean {
    return this.ngControl.valid && 
    (this.ngControl.dirty || this.ngControl.touched);
  }

  public get invalid(): boolean {
    return !this.ngControl.pending &&
      !this.ngControl.valid &&
      (this.ngControl.touched || this.ngControl.dirty);
  }
}

This is not a rigorous example, but it illustrates the use of @HostBinding, and I created the example in a StackBlitz

Mundt answered 24/9, 2019 at 0:34 Comment(2)
Unfortunately, this example won't work for structural directives and classes will have to be manually added in.Masse
Unfortunately, this require you have those classes as global css classes. since directive doesn't support scoped css (github.com/angular/angular/issues/17766)Demineralize
E
3

Directive example for opening and closing toggle on dropdown

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

@Directive({
    selector: '[appDropDown]',
})
export class DropsownDirective{

@HostBinding('class.open') isopen = false;
@HostListener('mouseenter') onMouseEnter(){
this.isopen = !this.isopen;
}
@HostListener('mouseleave') onMouseLeave(){
    this.isopen = !this.isopen;
}
}

Component add directive appDropDown

<div class="col-xs-12">
        <div class="btn-group" appDropDown>
        <button class="btn btn-primary dropdown-toggle">
            Manage Movie <span class="caret"></span>
        </button>
        <ul class="dropdown-menu">
            <li><a href="#">To watching List</a></li>
            <li><a href="#">Edit Movie</a></li>
            <li><a href="#">Delete Movie</a></li>
        </ul>
    </div>

Make sure to Include new directive in the @NgModule declarations

Erickaericksen answered 22/6, 2020 at 4:5 Comment(0)
B
1
export class CustomDirective {
   classname:string = "magenta";

   constructor(private renderer: Renderer2,
               private elementRef: ElementRef,
               service: SomService) {
   }

   addClass(className: string, element: any) {
        // make sure you declare classname in your main style.css
        this.renderer.addClass(this.elementRef.nativeElement, className);
   }

   removeClass(className: string, element: any) {
       this.renderer.removeClass(this.elementRef.nativeElement,className);
   }

}
Bergmans answered 28/2, 2018 at 1:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.