Advanced styling v4+💅
slides.com/gerardsans | @gerardsans
Google Developer Expert
Master of Ceremonies
Blogger
International Speaker
Angular Trainer
Community Leader
800
750
✨new in Angular✨
Angular Version
// index.html
<my-app ng-version="4.0.0-rc.4">
<div>
<h2>Hello Angular 4! 👋</h2>
</div>
</my-app>
Semantic Versioning
X . Y . Z
  MAJOR    MINOR     PATCH Â
Semantic Versioning
- v4 March 2017
- v5 Sept/Oct 2017
- v6 March 2018
- v7 Sept/Oct 2018
Components inheritance
Usage
<div>
<person name="John"></person>
<employee name="Tom" id="45231"></employee>
</div>
Implementation
@Component({
selector: 'person',
template: `<h4>Person: {{name}}</h4>`
})
export class Person {
@Input() name: string;
}
@Component({
selector: 'employee',
template: `<h4>Employee: {{name}}, id: {{id}}</h4>`
})
export class Employee extends Person {
@Input() id: string;
}
Introduction
Cascading Style Sheets
- Styling HTML Elements
- CSS rules
- Specificity and order
- Box model
HTML Page life-cycle
source: blog
Basic Example
<html>
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Hello World!</h1>
<p>This is my first CSS example</p>
</body>
</html>
CSS Rules
//style.css
h1 {
color: blue;
background-color: yellow;
border: 1px solid black;
}
p {
color: red;
}
Specificity and Order
-
Element style
-
Element id
-
class/attribute selectors
-
element selectors
-
last CSS rule wins
Box Model
source: blog
Angular
Components Tree
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 { }
CSS Styles Encapsulation
- Shadow DOM (emulated)
- Shadow DOM (native)
- Disabled
Shadow DOM (emulated)
@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>
Shadow DOM (native)
@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
@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>
Browser Support
safari
safari
Shadow vs light DOM
Shadow DOM
- Elements that the component creates and manages
- @Component
- template, templateUrls
Shadow DOM
@Component({
selector: 'song-track',
encapsulation: ViewEncapsulation.Native,
template: `
<div class="container">
<track-image image="image"></track-image>
<div class="track-information">
<track-title>{{track}}</track-title>
<track-artist>{{artist}}</track-artist>
</div>
</div>`
})
export class SongTrack { }
light DOM
- DOM elements created by the container. Not known at design time.
- Projected DOM elements
- <ng-content>
Light DOM
@Component({
selector: 'song-track',
encapsulation: ViewEncapsulation.Native,
template: `
<div class="container">
<ng-content select="track-image"></ng-content>
<div class="track-information">
<ng-content select="track-title"></ng-content>
<ng-content select="track-artist"></ng-content>
</div>
</div>`
})
export class SongTrack { }
Shadow vs Light DOM
<!-- Component (Shadow DOM) -->
<song-track
track="No Lie"
artist="Sean Paul, Dua Lipa">
</song-track>
<!-- Component (Light DOM) -->
<song-track2>
<track-image image="..."></track-image>
<track-title>No Lie</track-title>
<track-artist>Sean Paul, Dua Lipa</track-artist>
</song-track2>
Shadow DOM selectors
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>
Container Styling (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 { }
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 { ... }
Using Directives
ngClass (single)
<song-track ngClass="selected" class="disabled"></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 disabled"></song-track>
<song-track class="selected"></song-track>
ngClass (multiple)
<song-track ngClass="selected disabled">
<song-track [ngClass]="'selected disabled'">
<song-track [ngClass]="['selected', 'disabled']">
<song-track [ngClass]="{'selected': true, 'disabled': true}">
<song-track [ngClass]="{'selected disabled': true}">
<song-track class="selected disabled"></song-track>
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
host metadata (basics)
@Component({
host: {
'value': 'default', //'DOM-prop': 'value'
'[value]': "'default'", //'[DOM-prop]': 'expr'
'class': 'selected', //'DOM-attr': 'value'
'[class]': "'selected'", //'[DOM-attr]': 'expr'
'(change)': 'onChange($event)', // (event) : ...
'(window:resize)': 'onResize($event)', // (target:event) : ...
}
})
@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';
}
Using APIs
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;");
}
}
Renderer (all platforms)
import { ElementRef, Renderer } from '@angular/core';
@Component(...)
export class SongTrack {
constructor(
private element: ElementRef,
private renderer: Renderer
){
let elem = this.element.nativeElement;
renderer.setElementStyle(elem, "color", "blue");
renderer.setElementClass(elem, "selected", true);
}
}
Playground
Song Track Example
-
Encapsulation Modes
-
Inline, Template inline, External Styles
-
ngClass, ngStyle
-
Shadow DOM Selectors
-
Host bindings/listeners
Blog Post
Angular 4
New in Angular 4
- Improved compiler (AOT)
- uses TypeScript 2.2
- new bundles
- @angular/platform-server
- @angular/animations
New in Angular 4
- Add/update meta tag
- New EmailValidator
- Deprecated
- template, OpaqueToken
- ng-template, InjectionTokenÂ
ngIf changes
// Angular 2
<div *ngIf="flightInfo">{{flightInfo.name}}</div>
<div *ngIf="!flightInfo">Loading...</div>
// Angular 4
<div *ngIf="flightInfo; else noInfo">{{flight.name}}</div>
<ng-template #noInfo>
<div>Loading...</div>
</ng-template>
async pipe changes
// Angular 2
<div *ngIf="flightInfo$ | async">
{{(flightInfo$ | async)?.name}}
</div>
// Angular 4
<div *ngIf="flightInfo$ | async; let flight">
{{flight.name}}
</div>
<div *ngIf="flightInfo$ | async as flight">
{{flight.name}}
</div>
Examples
Danke Viel!
Angular Advanced Styling (v4+)
By Gerard Sans
Angular Advanced Styling (v4+)
In this talk we are going to cover all the different ways we can style our Angular Components. From introductory to advanced. We will learn how to style using Shadow DOM selectors, Light Dom, @HostBinding, ElementRef and more.
- 3,153