@HostBinding with a variable class in Angular

Angular

Angular Problem Overview


I have this code which sets a class on the host:

@HostBinding('class.fixed') true;

What I would like to do is make this a variable class that I can modify. How can I do this?

Angular Solutions


Solution 1 - Angular

This can't be made variable.

What you can do instead is to bind to the class property directly

@HostBinding('class') classes = 'class1 class2 class3';

Solution 2 - Angular

If you have a limited number of classes you can conditionally add each one:

@HostBinding('class.c1') get c1 () { return this.useC1; } 
@HostBinding('class.c2') get c2 () { return this.useC2; }

Note that .c1 and .c2 need to be defined outside the component.

Plunker

Solution 3 - Angular

I have to contradict the other answers, there is not a reason why binding class.foo shouldn't work. Actually, the following format works properly:

@HostBinding('class.foo') variableName = true;

If it doesn't work in your case, you might need to add a scope to your CSS class (you can see a discussion here).

The problem is that HostBinding only sees the host scope, it means that it only sees the classes and the id applied to the component itself, not to its children. So when you write your CSS, you need to specify that CSS belongs to the component itself (the host pseudoelement).

According to Angular documentation:

> Use the :host pseudo-class selector to target styles in the element that hosts the component (as opposed to targeting elements inside the component's template).

You can easily specify the host scope just adding :host before your CSS rule:

:host.foo { // It only matches <Component class="foo">
  /* Your CSS here */
}

In place of

.foo { // It matches any <element class="foo" /> inside <Component>
  /* Your CSS here */
}

If you want, I created a working Plunker that you can see clicking here

Solution 4 - Angular

Günter's answer isn't really helpful in case of already having some class names bound to a variable.

A good way to combine variable string class names with boolean style predefined class names is to use the classnames npm package.

Use it together with the @HostBinding and a setter function to get amazing results:

import * as classNames from 'classnames';

(...)

@HostBinding('class') get classes(): string {
  return classNames(this.getDynamicClassName(), {
    'is-open': this.isOpen,
    'has-children': this.hasChildren
  });
}

Solution 5 - Angular

@Input()
  class = '';

@HostBinding('attr.class')
get btnClasses() {
return [
    'btn',
    this.someClassAsString,
    this.enableLight ? 'btn-secondary-light' : '',
    this.class,
].filter(Boolean).join(' ');
};

You can hijack the class attribute with an input and add what you need to it in a hostbinding

Solution 6 - Angular

You can create some separated directives with class.

For example: I have a button in my page, and has may states: default, primary, danger and fluid. Button can have many different states, but I'll show you with theese three states because of huge amount of code. So let's get start it!

button.ts

//default button

@Directive({
    selector: '[appButtonDefault]'
})
export class ButtonDefaultDirective {

    // the name of the field is not important
    // if you put this directive to element, 
    // this element will have the class called 'button--default'

    @HostBinding("class.button--default")
    private defaultClass: boolean = true;
}


//primary button

@Directive({
    selector: '[appButtonPrimary]'
})
export class ButtonPrimaryDirective {

    // the name of the field is not important
    // if you put this directive to element, 
    // this element will have the class called 'button--primary'

    @HostBinding("class.button--primary")
    private primaryClass: boolean = true;
}


// danger button

@Directive({
    selector: '[appButtonDanger]'
})
export class ButtonDangerDirective {

    // the name of the field is not important
    // if you put this directive to element, 
    // this element will have the class called 'button--primary'

    @HostBinding("class.button--danger")
    private dangerClass: boolean = true;
}

@Directive({
    selector: '[appButtonFluid]'
})
export class ButtonFluidDirective {

    // the name of the field is not important
    // if you put this directive to element, 
    // this element will have the class called 'button--primary'

    @HostBinding("class.button--fluid")
    private fluidClass: boolean = true;
}


// you need to also create a component class,
// that import styles for this button
@Component({
    //just put created selectors in your directives
    selector: `[appButtonDefault], [appButtonPrimary], 
               [appButtonDanger], [appButtonFluid]`,
    styleUrls: ['<-- enter link to your button styles -->'],

    // it is required, because the content of <button> tag will disappear
    template: "<ng-content></ng-content>" 
})
export class ButtonComponent {}


// you don't have to do it, but I prefet to do it

@NgModule({
    declarations: [
        ButtonDefaultDirective,
        ButtonPrimaryDirective,
        ButtonDangerDirective,
        ButtonFluidDirective,
        ButtonComponent
    ],
    exports: [
        ButtonDefaultDirective,
        ButtonPrimaryDirective,
        ButtonDangerDirective,
        ButtonFluidDirective,
        ButtonComponent
    ]
})
export class ButtonModule {}

Don't forget to import ButtonModule to your app.module.ts file. It is very important.

app.component.html

<!-- This button will have the class 'button--default' -->
<button appButtonDefault>Default button</button>

<!-- But this button will have the class 'button--primary button--fluid' -->
<button appButtonPrimary appButtonFluid>Primary and fluid</button>

I hope it helps.

Solution 7 - Angular

There are lots of answers already, but none have mentioned NgClass. In my opinion, the most reliable and consistent way to do this is extending NgClass because it provides everything we need out-of-the-box-ish:

@Directive({ selector: '[myDirective]'})
export class MyDirective extends NgClass {
  constructor(
    _iterableDiffers: IterableDiffers,
    _keyValueDiffers: KeyValueDiffers,
    _ngEl: ElementRef,
    _renderer: Renderer2
  ) {
    super(_iterableDiffers, _keyValueDiffers, _ngEl, _renderer);
  }

  setClass() {
    this.ngClass = {
      underline: true,
      bold: true,
      italic: true,
      pretty: false
    };

    // or
    this.ngClass = ['asd', 'abc', 'def'];

    // or 
    this.ngClass = 'foo';
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionB HullView Question on Stackoverflow
Solution 1 - AngularGünter ZöchbauerView Answer on Stackoverflow
Solution 2 - AngularMark RajcokView Answer on Stackoverflow
Solution 3 - AngularCristian TraìnaView Answer on Stackoverflow
Solution 4 - AngularmatewkaView Answer on Stackoverflow
Solution 5 - AngularRico dos SantosView Answer on Stackoverflow
Solution 6 - Angulart.piwowarczykView Answer on Stackoverflow
Solution 7 - Angularj2L4eView Answer on Stackoverflow