Advanced Component styling with Angular (v4+)

slides.com/gerardsans |  @gerardsans

Text

Google Developer Expert

Master of Ceremonies

Blogger

International Speaker

Angular Trainer (v4+)

Community Leader

850

950

Angular In Flip Flops

Music Player Component

Layout

hover

Title/Artist

Selected

Song Track Component

@Component({
  selector: 'song-track',   // <song-track></song-track>
  template: ` 
    <div class="container">
      <track-image image="http://..."></track-image>
      <div class="track-information">
        <track-title>{{track}}</track-title>
        <track-artist>{{artist}}</track-artist>
      </div>
    </div>`
})
export class SongTrack { }

Component Styles

Inline Styles

@Component({
  selector: 'song-track',
  styles: [`.container { color: white; }`]
})
export class SongTrack { }

Template Inline Styles

@Component({
 template: `
   <style>
   .container { color: deepskyblue; }
   </style>   
   <div class="container">...</div>
 `
})
export class SongTrack { }

External Styles

//song-track.component.ts
@Component({
  styleUrls: ['src/shared.css'],
})
export class SongTrack { }

//shared.css
.container { ... }

Styling

Directives

Selecting a Track

ngClass (single)

<song-track ngClass="selected" class="large"></song-track>
<song-track [ngClass]="'selected'"></song-track>   
<song-track [ngClass]="['selected']"></song-track> 
<song-track [ngClass]="{'selected': true}"></song-track>

<song-track class="selected large"></song-track>
<song-track class="selected"></song-track>

ngClass (multiple)

<song-track ngClass="selected large">             
<song-track [ngClass]="'selected large'">      
<song-track [ngClass]="['selected', 'large']">   
<song-track [ngClass]="{'selected': true, 'large': true}">
<song-track [ngClass]="{'selected large': true}">

<song-track class="selected large"></song-track>

Styling

Song Title

ngStyle (single)

<song-track [ngStyle]="{'color': 'white'}" style="font-size: 12px;">
<song-track [ngStyle]="{'font-size.px': '12'}">
<song-track [ngStyle]="{'font-size': '12px'}">

<song-track style="color: white; font-size: 12px;">
<song-track style="font-size: 12px;">

ngStyle (multiple)

<song-track [ngStyle]="{'color': 'white', 'font-size': '12px'}">

<song-track style="color: white; font-size: 12px;">

Using host metadata

@Component.host (styling)

@Component({
  host: {
    //setting multiple values
    'class': 'selected disabled',
    'style': 'color: purple; margin: 5px;',
    
    //setting single values (using binding)
    '[class.selected]': 'true',    
    '[class.selected]': '!!selected', //add class if selected = true
    '[style.color]': '"purple"'   //expression must be a string
  } 
})
export class SongTrack { }

@HostBinding (styling)

@Component({
})
export class SongTrack {   
  //<host class="selected"></host>   
  @HostBinding('class.selected') selected = true;

  //<host style="color: red;"></host>     
  @HostBinding('style.color') color = 'red';
}

Shadow DOM

Shadow DOM

  • Isolated DOM
  • Scoped CSS
  • Composition
  • Productivity

Browser Support

safari

safari

Styles Encapsulation

Emulated

Shadow DOM

Emulated Shadow DOM

@Component({
  selector: 'song-track',
  encapsulation: ViewEncapsulation.Emulated
})

<head>
  <style>
    .container[_ngcontent-ikt-1] { ... } 
  </style>
</head>
<body>
  <my-app>
    <song-track _nghost-ikt-1>
      <div _ngcontent-ikt-1 class="container"></div>
    </song-track>
  </my-app>
</body>

Native Shadow DOM

Native Shadow DOM

@Component({
  selector: 'song-track',
  encapsulation: ViewEncapsulation.Native
})

<body>
  <my-app>
    <song-track>    
      ▾ #shadow-root (open)    
        <style>.container { ... }</style>   
        <div class="container"></div>
    </song-track>
  </my-app>
</body>

Disabled  Shadow DOM

Disabled Shadow DOM

@Component({
  selector: 'song-track',
  encapsulation: ViewEncapsulation.None
})

<head>
  <style>.container { ... }</style>
</head>
<body>
  <my-app>
    <song-track>    
      <div class="container"></div>
    </song-track>
  </my-app>
</body>

Styling

Shadow DOM

Container Styling (:host)

@Component({
 styles: [`
  :host { color: black; }          
  :host(.selected) { color: red; } 
 `]
})
export class SongTrack { }


<song-track></song-track>
<song-track class="selected"></song-track>

Context Styling (:host-context)

:host-context(.theme) { color: red; }   
:host-context(#player1) { color: red; }


<div class="theme">
 <song-track></song-track>
</div>

<div id="player1">
 <song-track></song-track>
</div>

Overriding Styles (/deep/)

@Component({
 styles: [`
  :host  /deep/ .h3 { color: red; }
  :host   >>>   .h4 { color: purple; }
 `],
 template: `
    <div class="container">
      <track-image image="http://..."></track-image>
      <div class="track-information">
        <track-title>{{track}}</track-title>     //<h3><ng-content></h3>
        <track-artist>{{artist}}</track-artist>  //<h4><ng-content></h4>
      </div>
    </div>`
})
export class SongTrack { }

Angular

Platforms

ElementRef (only browser)

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

@Component(...)
export class SongTrack {
  constructor(private element: ElementRef){
    let elem = this.element.nativeElement;

    elem.style.color = "blue";
    elem.style.cssText = "color: blue; ..."; // multiple styles
    elem.setAttribute("style", "color: blue;"); 
  }
}

Renderer2 (all platforms)

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

@Component(...)
export class SongTrack {
  constructor(
     private element: ElementRef,
     private renderer: Renderer2
  ){
    let elem = this.element.nativeElement;

    renderer.setStyle(elem, "color", "blue");
    renderer.addClass(elem, "selected", true);
  }
}

Playground

Song Track Example

  • Encapsulation Modes

  • Inline, Template inline, External Styles

  • ngClass, ngStyle

  • Shadow DOM Selectors

  • Host bindings/listeners

Blog Post

Thanks!