Inject Style Declarations Using Hostbinding in Angular
Asked Answered
V

2

13

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

Vue answered 11/9, 2017 at 7:57 Comment(5)
What styles did you try to inject? What does "does not work" mean exactly?Mayest
nothing happens, no erors and the styles(background and color) do not get applied. I think that my syntax might be wrong or the style attribute might not be available for binding to using the HostBinding decorator.Vue
Please add the CSS style you try to apply and the HTML where you want it applied (minimal example that allows to reproduce).Mayest
sorry about that I am not that used to stackoverflow, I added all the code and a working plunker example, hope that helps.Vue
Thanks, Plunker is very helpful.Mayest
B
20

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

Berga answered 11/9, 2017 at 8:53 Comment(6)
The plunker says /* this does not work */, any updateGrantham
Copying to a new Plunker would probably do to get the Angular version updated. I'll have a look when I have a bit of time (no guarantees)Mayest
@BenTaliadoros Demo was broken. Fixed it.Cavell
@GünterZöchbauer Yes, here you go - stackblitz.com/edit/style-hostbinding-demoCavell
How can you set multiple definitions for the same style, e.g. cursor: grab; cursor: -webkit-grabBradfordbradlee
I don't think you can. Better to set/clear a class and use CSS instead.Mayest
B
0

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.

Biebel answered 29/7, 2019 at 13:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.