23

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
    }
  }
}
1
  • 1
    I think you know how to add styles with host binding but classes are not supported inside directives I guess :/ stackoverflow.com/questions/35915433/… Commented Jan 7, 2017 at 11:11

4 Answers 4

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);
   }

}
Sign up to request clarification or add additional context in comments.

2 Comments

What is host element?
@Karty the element on which the directive is attached to
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

2 Comments

Unfortunately, this example won't work for structural directives and classes will have to be manually added in.
Unfortunately, this require you have those classes as global css classes. since directive doesn't support scoped css (github.com/angular/angular/issues/17766)
4

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

Comments

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);
   }

}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.