57

I am currently learning Angular 2. I understood how to use the Angular Renderer to set an ElementStyle, but now I would like to use the Renderer method:

setElementClass(renderElement: any, className: string, isAdd: boolean) : void

My question is how can I import a CSS class to my attribute directive? Do I have to convert my CSS class to JSON?

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Daniel Hoppe Alvarez
  • 1,298
  • 1
  • 13
  • 18

4 Answers4

121

Original OP was asking how to use Renderer. I've included the @HostBinding for completeness.

Using @HostBinding

To add a class to an element you can use @HostBinding

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

@Directive({
  selector: '[myDirective]',
})
export class MyDirective {

  @HostBinding('class')
  elementClass = 'custom-theme';

  constructor() {
  }
}

Using @HostBinding with multiple classes

To make multiple classes more comfortable to use, you can use the ES6 getter and join the classes together before returning them:

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

@Directive({
  selector: '[myDirective]',
})
export class MyDirective {
  protected _elementClass: string[] = [];

  @Input('class')
  @HostBinding('class')
  get elementClass(): string {
      return this._elementClass.join(' ');
  }
  set(val: string) {
      this._elementClass = val.split(' ');
  }

  constructor() {
      this._elementClass.push('custom-theme');
      this._elementClass.push('another-class');
  }
}

Using Renderer

The more low level API is Renderer2. Renderer2 is useful when you have a dynamic set of classes you would like to apply to an element.

Example:

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

@Directive({
  selector: '[myDirective]',
})
export class MyDirective {

  constructor(private renderer: Renderer2, hostElement: ElementRef) {
    renderer.addClass(hostElement.nativeElement, 'custom-theme');
  }
}
developer033
  • 24,267
  • 8
  • 82
  • 108
cgatian
  • 22,047
  • 9
  • 56
  • 76
  • I'm using for loop of classes array, is that practical? – Bear0x3f Sep 21 '17 at 03:21
  • For multiple classes you should perform a loop. If you take a look at the [source](https://github.com/angular/angular/blob/master/packages/platform-browser/src/browser/browser_adapter.ts#L242) you'll see Angular calls the [classList.add](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList) method. While this native method supports multiple arguments, the Angular implementation only allows passing one class name at a time. – cgatian Nov 06 '17 at 14:11
  • 1
    pushing classes from the constructor doesn't seem to work. But it works from ngAfterViewInit – Adrien H May 12 '19 at 13:37
  • 1
    The setter never seems to get called, causing any initial classes of the host to be lost :-( – Benjamin Kindle Jul 22 '19 at 16:38
  • 1
    You don't need to DI the renderer or manipulate the existing classes instead use `@HostBinding('class.your-class')` - https://stackoverflow.com/a/58071653/1148107 – mtpultz Sep 24 '19 at 00:41
  • I am trying to remove the class in directive 'error' `HostListener`. Suprisingly for me, the remove is successfull, but the layout is not changed - the removed CSS class is still present there. – Alexander Feb 12 '23 at 15:14
12

Why would you want to use the Renderer or Renderer2 class? The preferred way to do this in a directive is to use the @HostBinding decorator.

Example:

import { HostBinding } from '@angular/core';

@Directive({
    selector: '[myDirective]'
})
export class MyDirective {

    @HostBinding('class')
    className = 'my-directive-css-class';
}
Liam
  • 27,717
  • 28
  • 128
  • 190
csnate
  • 2,789
  • 1
  • 14
  • 4
  • 1
    This is useful if you want to add a class based on some other property, i.e. dynamically: – dannrob Nov 06 '17 at 11:46
  • 7
    I think this is only useful to completely replace the classes that may already be defined in the HTML. How would I *add* a class with this method? Renderer seems more flexible. – Simon_Weaver Jun 10 '18 at 02:35
  • 5
    This syntax is better : `@HostBinding('class.isPlaying') class_isPlaying = true;` (see this https://stackoverflow.com/a/35183074/16940). It will toggle classes as opposed to just wiping out the whole attribute. – Simon_Weaver Jun 10 '18 at 02:38
  • 3
    @Simon_Weaver That syntax probably *was* better, and still is a good choice if you need to toggle one class based on a property. It seems however that in recent versions of Angular (e.g. 10) the `@HostBinding('class')` does not wipe out the whole attribute. It behaves just like the [class] binding in the template, in that you can give it classes as a space delimited string, an array of strings, or even conditional classes as an object (note however, that it does not detect changes within an array or object). ref: https://angular.io/guide/attribute-binding#binding-to-the-class-attribute – Superole Nov 17 '20 at 12:39
10

Just another way to do it but easier and more understandable to me.

You let me know what you think

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

@Directive({
  selector: '[whatever]',
  host: {
    // These are like ngClass class condition values
    '[class.custom-class1]': 'true', // Default class for example
    '[class.custom-class2]': 'foo === "expectedValue"', // Predicate1
    '[class.custom-class3]': 'foo !== "expectedValue"', // Predicate2
  },
})
export class WhateverDirective {
  @Input() foo: string;
}
aadiene
  • 327
  • 3
  • 7
6

Example of how to use Renderer and ElementRef to add css class to element.

@Directive({
   selector: '[whatever]'
})
class WhateverDirective {
   constructor(renderer: Renderer, el: ElementRef) {
       renderer.setElementClass(el.nativeElement, 'whatever-css-class', true);
   }
}

The whatever-css-class is defined in a css file, which is referenced in html

Liam
  • 27,717
  • 28
  • 128
  • 190
jaguwalapratik
  • 406
  • 2
  • 10
  • Yes absolutely, so far I understood the setElementClass function, but where lays the whatever-css-class, an attribute directive doesn't have an css file or is there a possibility to add one ? – Daniel Hoppe Alvarez Sep 22 '16 at 13:00
  • Right, so what you can do is define class in css file which you are referencing in html, so that way it will be applied to the element. – jaguwalapratik Sep 22 '16 at 13:04
  • [Renderer](https://angular.io/api/core/Renderer) class is now deprecated. Use [Renderer2](https://angular.io/api/core/Renderer2) instead. – matewka Jul 06 '17 at 20:52
  • 1
    You don't need to DI the renderer instead use `@HostBinding('class.your-class')` - https://stackoverflow.com/a/58071653/1148107 – mtpultz Sep 24 '19 at 00:36