5

I am fairly new to Angular and came from React.js background.

I have made a simple grid component like below:

grid.component.js

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

@Component({
  selector: 'app-grid',
  template: `
    <div [ngStyle]="styles()" [ngClass]="passClass">
      <ng-content></ng-content>
    </div>
  `,
  styles: [`
    div {
      display: flex;
    }
  `]
})
export class GridComponent implements OnInit {
  @Input() direction: string;
  @Input() justify: string;
  @Input() align: string;
  @Input() width: string;
  @Input() passClass: string;

  constructor() { }

  ngOnInit() {
  }

  styles() {
    return {
      'flex-direction': this.direction || 'row',
      'justify-content': this.justify || 'flex-start',
      'align-items': this.align || 'flex-start',
      ...(this.width && { width: this.width })
    };
  }
}

And I want to use it in other components like below: aboutus.component.html

<app-grid passClass="about-us page-container">
  <app-grid direction="column" passClass="left">
    <div class="title blue bold">
      An open community For Everyone
    </div>
    <div class="large-desc grey">
      This conference is brought to you by
      the Go Language Community in
      India together with the Emerging
      Technology Trust (ETT). ETT is a non-
      profit organization, established to
      organize and conduct technology
      conferences in India. It’s current
      portfolio includes
    </div>
  </app-grid>
</app-grid>

aboutus.component.sass

.about-us
  position: relative
  .left
    width: 50%
    &:after
      bottom: 0
      right: 0
      z-index: 0
      margin-right: -5vw
      position: absolute
      content: url(../../assets/images/footer.svg)

But, what happens is the CSS attached with the second component will not work.

I know a little bit about CSS isolation but could not understand if it affects here.

P.S.: Please feel free to provide feedback to things outside of scope this question as well.

2 Answers 2

3

It is not possible to pass CSS classes as variables in your template. So if your expectation in aboutus.component.html was to be able to pass the left CSS class as a variable in your template, that will not work.

There are a few things I can point out that will hopefully help:

  1. If you want to modify a CSS class that is internal to a component from outside that component, one option is to use ng-deep.

  2. In your particular case, I don't think ng-deep is necessary. I'd suggest to drop the div element within the app-grid component and instead apply the styles to the host element using @HostBinding decorator. With that approach you can drop the passCss altogether because now wherever you use yourapp-grid component you can style that component in CSS using the app-grid selector.

    grid.component.ts:

    import { Component, OnInit, Input, HostBinding, SafeStyle } from '@angular/core';
    
    @Component({
      selector: 'app-grid',
      template: `<ng-content></ng-content>`,
      styles: [`
        :host {
          display: flex;
        }
      `]
    })
    export class GridComponent implements OnInit {
      @Input() direction: string;
      @Input() justify: string;
      @Input() align: string;
      @Input() width: string;
    
      constructor(private sanitizer:DomSanitizer) { }
    
      ngOnInit() {
      }
    
      @HostBinding('style')
      styles(): SafeStyle {
        const styles = `
          flex-direction: ${this.direction || 'row'};
          justify-content: ${this.justify || 'flex-start'};
          align-items: ${this.align || 'flex-start'};
       `;
        return this.sanitizer.bypassSecurityTrustStyle(styles);
      }
    }
    

    aboutus.component.sass:

      app-grid {
        // You can style the host element of a component
        // just like any native HTML element and without
        // needing to use `ng-deep`
      }
    
  3. You may also want to look into CSS Custom Properties. Custom CSS properties are not shielded by view-encapsulation. This gives you the ability to create a CSS API for a component, if you will, and those properties can be used anywhere within a component.

    aboutus.component.sass

    app-grid {
      --image: url(../../assets/images/footer.svg)
    }
    

    grid.component.sass

    div {
      content: var(--image);
    }
    
Sign up to request clarification or add additional context in comments.

3 Comments

Your solution seems to be the perfect one. But, there is some issue with the code you mentioned. @HostBinding('style') styles() is not working and I tried binding to @HostBinding('attr.style') then it was said to be unsafe. I had to do a single style binding like @HostBinding('style.flex-direction'). Can you please update that and I will mark your answer as correct. Thanks for the answer.
Oh right, you have to use the DomSanitizer as shown in this answer. I will amend my answer.
Awesome! Thanks again for the help.
2

If you want style some element in other component use :host and /deep/ modifiers(deprecated - Alternative to /deep/). More about this feature you can read in documentation

In your case this should work:

:host /deep/ {
    .left {
        width: 50%
        &:after {
          bottom: 0
          right: 0
          z-index: 0
          margin-right: -5vw
          position: absolute
          content: url(../../assets/images/footer.svg)
       }
    }
}

You can also disable encapsulation for this component:

@Component({
  selector: 'app-grid',
  template: `
    <div [ngStyle]="styles()" [ngClass]="passClass">
      <ng-content></ng-content>
    </div>
  `,
  styles: [`
    div {
      display: flex;
    }
  `],
  encapsulation: ViewEncapsulation.None
})

5 Comments

Thanks, In the aboutus component css?
Isn't /deep/ deprecated in angular 7?
You're right. Unfortunately I don't know any alternative
stackoverflow.com/questions/51708972/alternative-to-deep - some more informations about this deprecation in Angular
I am skeptical about using ViewEncapsulation.None I am not sure if It can affect other things. Like if I have div selector in another component, I guess it will be affected by that right?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.