1

I would like to know if it's possible to style a component through the 'styles' property of angular's @component object.

Here is an example of what I'm trying to achieve, pay attention to the {{ratio}} variable in 'styles' property.

@Component({
 selector: 'img-thumb',
 templateUrl: './img-thumb.component.html',
 styleUrls: ['./img-thumb.component.css'],
 styles: [`
   :host:after{
     content: '';
     display: block;
     padding-bottom: {{ratio}};
   }
 `],
 host: {
  '[style.height.px]' : 'height',
  '[style.padding.px]' : 'gap'
 } 
})

export class ImgThumbComponent implements OnInit {

 @Input() height : any
 @Input() gap : any
 @Input() ratio : any

 //More code...

 }

Basically I'm trying to implement :after on the host with a variable. If it's possible, I prefer to implement it inside the .ts file of the component.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • The interpolation syntax is *only* for templates, not styles. – jonrsharpe Oct 14 '17 at 11:10
  • 1
    @jonrsharpe Thanks for your quick answer. I use the interpolation syntax to demonstrate what I'm trying to achieve. – Ran Roslan Bobkov Oct 14 '17 at 11:23
  • Do you access to the `img-thumb.component.html` file? If you are comfortable in modifying that file, I would use the values of the @input variables directly on the HTML element using the `[ngStyle]` property biding syntax. – Miquel Canal Oct 14 '17 at 11:56
  • @Treeindev Yes, I can modify the html file as much as I want. though I'm not sure how its possible to add :after on the host component using [ngStyle]? – Ran Roslan Bobkov Oct 14 '17 at 13:06
  • If you want to access the host from the class, see e.g. https://stackoverflow.com/a/34643330/3001761 – jonrsharpe Oct 14 '17 at 13:59
  • Thanks, but I won't be able to pass a variable to it, this is main problem - add an :after to the host, with a value of a @input. – Ran Roslan Bobkov Oct 14 '17 at 14:11
  • @RanRoslanBobkov, you're right. It is not possible to specify inline styles for pseudo-elements. In that case you can use the renderer approach to dynamically generate the styles. I'm writing a new answer with this. – Miquel Canal Oct 14 '17 at 14:14

1 Answers1

1

As it is not possible to add inline pseudo-element styles, one workaround would be to use Angular's renderer2 methods to create a new <style> tag and append it to the DOM at runtime. Here's a component:

import { Component, ElementRef, ViewChild, OnInit, Renderer2 } from '@angular/core';

@Component({
    selector: 'app-component',
    template: '<div #stylesContainer></div>'
})
export class singleComp implements OnInit{
    // Get destination container
    @ViewChild("stylesContainer") private stylesDist: ElementRef;

    // Define variable values
    height: string = "150px";
    gap: string = "30px";
    ratio: string = "10px";

    // Create the actual CSS style and store it in variables
    styleClass = ".class{ height: "+this.height+"; padding: "+this.gap+"}";
    pseudoStyle = ".class:after{content: '';display: block;padding-bottom: "+this.ratio+"}";

    constructor(private renderer: Renderer2) {}

    ngOnInit() {
        // Dynamically create the CSS tags
        const classStyle = this.renderer.createText(this.styleClass);
        const pseudoElementStyle = this.renderer.createText(this.pseudoStyle);

        // Insert the previously defined style into a new <style> element
        const newStyleElement = this.renderer.createElement('style');
        this.renderer.appendChild(newStyleElement, classStyle);
        this.renderer.appendChild(newStyleElement, pseudoElementStyle);
        this.renderer.appendChild(this.stylesDist.nativeElement, newStyleElement);
    }
}

In the component above the variables height, gap and ratio are hardcoded, but you can get them via @input. The HTML template contains a div with the local reference #stylesContainer this will be the destination element on where to dynamically append the styles.

When the component initialises, it generates a new <style> tag containing the dynamic styles including the pseudo-element ones. Then using the Renderer2 from Angular, it appends the new <style> tag back into the <div #stylesContainer> which you get from using @ViewChild

Miquel Canal
  • 992
  • 1
  • 17
  • 25
  • Hi, Thanks a lot for your time, seriously appreciate it. Your solution solves the problem, but, it created a whole new html inside the component, with all the dependencies that my index.html holds. This causes two main problems: 1. Since I'm using this component alot through the whole application, it will affect the performance very badly. 2. The html created holds its own head and body tags, this might be an SEO issue according to some colleagues in my company. – Ran Roslan Bobkov Oct 15 '17 at 20:07
  • It shouldn't create a new whole html inside, but just create a new ` – Miquel Canal Oct 16 '17 at 07:44