Inject Style Declarations Using Hostbinding in Angular

27.6k views Asked by At

Do you guys know how I can batch inject style declarations in a component using the @HostBinding decorator? What I am trying is:

@HostBinding('style')
get style(): CSSStyleDeclaration {
  return {
    background: 'red',
    color: 'lime'
  } as CSSStyleDeclaration;
}

In my understanding this should inject the background and color style to the component, but it does not...

I can control individual style declarations like this:

@HostBinding('style.background') private background = 'red';

but I would like to do it for all of them, please help :P

this is the full code:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello world!</h2>
    </div>
  `,
})
export class App {

  // This works
  @HostBinding('style.color') private color = 'lime';

  /* This does not work
  @HostBinding('style')
  get style(): CSSStyleDeclaration {
    return {
      background: 'red'
    } as CSSStyleDeclaration;
  }
  */

  constructor() {}
}

and a working plunker: https://plnkr.co/edit/CVglAPAMIsdQjsqHU4Fb?p=preview

2

There are 2 answers

6
Günter Zöchbauer On BEST ANSWER

You need to pass the same value you would add to an element like <div style="..."> and sanitize the styles

  @HostBinding('style')
  get myStyle(): SafeStyle {
    return this.sanitizer.bypassSecurityTrustStyle('background: red; display: block;');
  }

  constructor(private sanitizer:DomSanitizer) {}

working demo

0
Mario Petrovic On

Here is a solution that can cover if you want to pass multiple css styles as string or as object with cammelCase conventions:

Parent HTML

<app-button [style]="styleFromParent">Some button</app-button>

Parent component has styleFromParent property and it has simulation if that property is changed at some point:

Parent Component TS

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-site-panel',
  templateUrl: './site-panel.component.html',
})
export class SitePanelComponent implements OnInit {
  constructor(private _detectChanges: ChangeDetectorRef) {}
  styleFromParent = { marginTop: '10px', marginLeft: '50px' };

  ngOnInit() {
    setTimeout(() => {
      this.styleFromParent = { marginTop: '20px', marginLeft: '1px' };

      this._detectChanges.detectChanges();
    }, 2000);
  }
}

Child HTML

<ng-content></ng-content>

Child Component TS

import { Component, OnInit, HostBinding, Input } from '@angular/core';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';

@Component({
  selector: 'app-button',
  templateUrl: './button.component.html',
})
export class ButtonComponent implements OnInit {
  @HostBinding('style') baseStyle: SafeStyle;

  @Input()
  set style(style: string | object) {
    let mappedStyles = style as string;

    if (typeof style === 'object') {
      mappedStyles = Object.entries(style).reduce((styleString, [propName, propValue]) => {
        propName = propName.replace(/([A-Z])/g, matches => `-${matches[0].toLowerCase()}`);
        return `${styleString}${propName}:${propValue};`;
      }, '');

      this.baseStyle = this.sanitizer.bypassSecurityTrustStyle(mappedStyles);
    } else if (typeof style === 'string') {
      this.baseStyle = this.sanitizer.bypassSecurityTrustStyle(mappedStyles);
    }
  }

  constructor(private sanitizer: DomSanitizer) {}

  ngOnInit() {}
}

Above you can see that baseStyle has HostBinding to style component binding. When style input is passed setter will triger, check if string or object is passed, parse it to string and sanitize that css and assign it to baseStyle thus host style will change.