Łukasz Ostrowski
Łukasz Ostrowski

Reputation: 1020

Pass class to children component host

I'm used to React approach where I could basically inject anything anywhere.

I have a dumb Button or app-button component in Angular. It's inline-block (:host class), so its width depends on the content. In this case, I can't override its params like display: block or set width. I can do it manually by adding new @Input per param ([display], [width]) but it's not really nice.

The behavior I want to have is an input/directive on this component to provide an explicitly inject class to the inside of the child component.

In React I would just add prop with the class name and assign it or pass some inline styles, depending on what style system I'm using.

Is there any way/lib/util to handle this problem?

Upvotes: 9

Views: 26640

Answers (3)

Alexander Abakumov
Alexander Abakumov

Reputation: 14539

You should not write CSS rules for a child component elements in a parent component, since an Angular component is a self-contained entity which should explicitly declare what is available for the outside world. If child layout changes in the future, your styles for that child component elements scattered across other components' SCSS files could easily break, thus making your styling very fragile. That's what ViewEncapsulation is for in the case of CSS. Otherwise, it would be the same if you could assign values to private fields of some class from any other class in Object Oriented Programming.

Therefore, what you should do is to define a set of classes you could apply to the child host element and implement how the child responds to them.

Technically, it could be done as follows:

// child.component.html:
<span class="label-1"></span>

// child.component.scss:
:host.child-color-black {
    .label-1 {
        color: black;
    }
}

:host.child-color-blue {
    .label-1 {
        color: blue ;
    }
}

// parent.component.html:
<child class="child-color-black"></child>
<child class="child-color-blue"></child>

In other words, you use :host pseudo-selector provided by Angular + set of CSS classes to define possible child styles in child component itself. You then have the ability to trigger those styles from outside by applying pre-defined classes to the <child> host element.

Upvotes: 6

Andi
Andi

Reputation: 541

Today there are a couple of other possibilities:

:host-context(.some-class-name) This lets you react on some outer class

::ng-deep css-expression{ xx } This way you can define a class in the parent that will be available in it's children.


Example:

parent.component.html

<app-button class="theme-blue"> my button </app-button>

button.component.css

:host-context(.theme-blue) button {
   background-color: blue;
}

One may as well check out this very well guide: https://alligator.io/angular/styles-between-components-angular/

Upvotes: 3

Tomasz Kula
Tomasz Kula

Reputation: 16837

Because of the Angular's ViewEncapsulation you cannot do it the way React does.

Your best bet is to style the button :host element directly. This way you can override it with classes defined in the parent component.

app-button.component.ts

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

@Component({
  selector: 'app-button',
  template: `
    I am red by default
  `,
  styles: [`
  :host {
    background-color: red;
  }
  `]
})
export class ButtonComponent {}

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
  <app-button class="button"></app-button>
  `,
  styles: [
    `.button { 
       background-color: blue; 
     }
    `
  ]
})
export class AppComponent  {}

Live demo

Upvotes: 3

Related Questions