Angular Styling für Fortgeschrittene
angular styling advanced | Gerard Sans • | 14 Minuten
In diesem Leitfaden wollen wir die verschiedenen Optionen abdecken, die bei der Gestaltung von Angular-Komponenten und Direktiven zur Verfügung stehen. Dieser Artikel ist an Fortgeschrittene gerichtet.
Wir werden folgendes behandeln:
- Angular Verkapselung Modi: emuliert, nativ, deaktiviert
- Browser-Support, Shadow DOM vs Light DOM.
- @Component Styling Metadaten: Inline, Template Inline, Externe Entwürfe.
- Verwendung von
ngClass
undngStyle
Direktiven - Shadow Dom Selektoren:
:host
,:host()
,:host-context()
, :host /deep/selector, :host»>selector - Verwendung von
@Component.host
und@HostBinding
- Verwendung von
ElementRef
und nativeElement APIs (Web). - Verwendung von Renderer und setElementClass/setElementStyle APIs (Web, Server, WebWorker).
- CSS-Styles-Spezifität und Ausführungsreihenfolge
Du kannst den finalen Code mit diesem Plunker erkunden.
Einführung
Angular Anwendungen zu entwerfen ging noch nie flexibler. Angular-Component-Architecture bietet ein neues Entwurfsmodell, das Component Styles durch die Verwendung von Shadow DOM (emuliert oder nativ) einer Technologie aus der Web Components Spezifikation isoliert. Entwürfe werden für jede Komponente skizziert, so dass sie andere Bereiche der Benutzeroberfläche nicht beeinflussen können.
Für diesen Beitrag werden wir eine Komponente verwenden, um Song-Tracks zu veröffentlichen, die einige verschiedene Styling-Optionen haben. aDiese Komponente wird das Cover, den Titel und den Künstler für einen Song rendern.
@Component({
selector: 'song-track', // <song-track></song-track>
})
export class SongTrack { }
Angular Encapsulation
Lass uns schnell alle verfügbaren Encapsulation-Modi erkunden, bevor wir die verschiedenen Styling-Ansätze weiter erforschen.
Emuliert (Standard)
Bei der Verwendung dieses Modus wird Angular jede Komponente mit zwei eindeutigen Attributen identifizieren: _nghost-*
und _ngcontent-*
.
Alle Komponenten-Styles werden dem Kopf hinzugefügt, indem diese Attribute verwendet werden, um die Styles wie im folgenden Beispiel zu isolieren.
<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>
Beachte die Atrribute, die dem root
und `content unserer Komponente fett hinzugefügt wurden.
Du kannst diesen Modus aktivieren, indem du folgenden Code unten verwendest
@Component({
selector: 'song-track',
encapsulation: ViewEncapsulation.Emulated
})
„Emulierte Verkapselung hat die beste Unterstützung für alle aktuellen Browser.“
Native Encapsulation (Shadow DOM)
Bei diesem Encapsulation-Mode nutzen wir den Native Shadow DOM
für eine bestimmte Komponente.
Je nach Browser ist dies v1 der Spezifikation (Chrome).
@Component({
selector: 'song-track',
encapsulation: ViewEncapsulation.Native
})
Diese Komponente wird als Resultat folgendes rendern.
<body>
<my-app>
<song-track>
▾ #shadow-root (open)
<style>.container { ... }</style>
<div class="container"></div>
</song-track>
</my-app>
</body>
Beachte, wie die Styles jetzt unter #shadow-root
eingekapselt sind.
Die spezifischen Styling-Optionen werden wir später behandeln.
„Native Verkapselung wird in einigen Browsern noch nicht unterstützt. Überprüfe hier die funktionierenden Browser.“
Deaktivierung der Verkapselung
Wir können auch die Verkapselung für eine bestimmte Komponente komplett deaktivieren.
@Component({
selector: 'song-track',
encapsulation: ViewEncapsulation.None
})
Durch die Verwendung dieses Modus wird Angular alle definierten Styles dem Kopf hinzufügen, so dass Styles über Komponenten mit dieser Verkapselung geteilt werden können.
Native Shadow DOM Browser-Support
Native Shadow DOM
wird zu diesem Zeitpunkt noch nicht unterstützt.
Siehe unten emulierten und nativen Browsersupport-Vergleiche, nebeneinander.
*„Welche Browser das Feature aktuell unterstützen kannst du bei canIuse.com nachschauen.“
„ProTip: Schaue, ob dein Browser native Encapsulation unterstützt, bevor du sie aktivierst. :)“
Shadow DOM vs Light DOM
Beim Entwerfen unserer Komponenten kann es helfen, zwischen ShadowDOM
und Light DOM
zu unterscheiden.
Shadow DOM: Alle lokalen DOM-Elemente, die eine Komponente erstellt oder verwaltet. Dazu gehören auch alle untergeordneten Komponenten.
<song-track title="No Lie" artist="Sean Paul..."></song-track>
@Component({
selector: 'song-track',
template: `
<track-title>{{track}}</track-title>
<track-artist>{{artist}}</track-artist>`
})
export class SongTrack { }
Light DOM: Alle Childen DOM-Elemente einer Komponente. Auch als projizierter Inhalt (ng-content).
<song-track>
<track-title>No Lie</track-title>
<track-artist>Sean Paul, Dua Lipa</track-artist>
</song-track>
@Component({
selector: 'song-track',
template: `<ng-content></ng-content>`
})
export class SongTrack { }
@Component Styling über Metadaten
Wollen wir spezifische Styles zu unser Angular Komponente hinzufügen, nutzen wir hierfür den @Component()
Decorator und die Property style
.
„Angular wird die Styles exakt in der angegebenen Reihenfolge anfügen.“
Verwendung von Inline-Styles
Hier fügen wir unsere Styles direkt in der TypeScipt-Datei welche unsere Komponente definiert hinzu.
Da styles
ein Array ist, können wir hier beliebig viele Strings anfügen.
@Component({
selector: 'song-track',
styles: [`.container { color: white; }`]
})
export class SongTrack { }
Verwendung von Inline-Style-Templates
Natürlich ermöglicht es Angular auch direkt mit Inline-Styles zu arbeiten.
@Component({
template: `
<style>
.container { color: deepskyblue; }
</style>
<div class="container">...</div>
`
})
export class SongTrack { }
Verwendung einer externen Style-Datei
Der sauberste Weg ist es, unsere Styles der Komponente in eine extra Datei auszulagern. Weiterer Vorteil an diesem Vorgehen: Wir können mit Prepozessoren wie SCSS oder LESS arbeiten.
song-track.component.css
.container { ... }
song-track.component.ts
@Component({
styleUrls: ['./song-track.component.css'],
})
export class SongTrack { }
Als Teil der CSS-Sezifikation können wir auch @import
verwenden, um Styles aus anderen Stylesheets zu importieren.
Diese müssen im Stylesheet irgendwelchen Styile-Regeln vorangehen.
Siehe @import.
Importe werden in den Header nach dem Stylesheet eingefügt.
@import 'common.css';
.container { ... }
Verwendung von NgClass- und ngStyle-Direktiven
Wir können ngClass
und ngStyle
Direktiven verwenden, um unsere Komponente dynamisch zu gestalten.
Lass uns einige Verwendungen anschauen
<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>
Beachte, dass ngClass
mit vorhandenen Klassenattributen kombiniert werden kann ohne Bindungen zu verwenden.
Um mehrere Klassen zu erreichen, können wir die erweiterte Syntax mit einigen interessanten Variationen nutzen.
<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}">
Für ngStyle
können wir das gleiche machen, aber da wir Paare von Eigenschaften und Werten brauchen, gibt es weniger Optionen.
<song-track [ngStyle]="{'color': 'white'}" style="margin: 5px;"><song-track [ngStyle]="{'font-size.px': '12'}">
<song-track [ngStyle]="{'font-size': '12px'}">
<song-track [ngStyle]="{'color': 'white', 'font-size': '12px'}">
Beachte die erweitere Einheiten-Syntax, die mit vorhandenen CSS-Messeinheiten übereinstimmt. Um mehrere Styles anzuwenden, kannst du weitere Eigenschaften hinzufügen.
Verwendung von Shadow DOM Selektoren
Bei der Verwendung von emulierter oder nativer Verkapselung haben wir Zugriff auf einige interessante CSS-Selektoren, die nur für Shadow DOM
verfügbar sind.
Entwerfen unserer Container (aka Host)
Falls wir auf unseren Container (eventuell mit Verbindung mit anderen Selektoren) zugreifen müssen, können wir den :host
Pseudo-Klassen-Selektor benutzen.
:host { color: black; } /* <song-track> */
:host(.selected) { color: red; } /* <song-track class="selected"> */
Das erste Beispiel stimmt mit dem Song-Track-Element überein und fügt die Farbe zu den Styles hinzu. Das zweite Beispiel stimmt mit Song-Track-Elementen mit der ausgewählten Klasse überein.
Entwerfen je nach Vorgängern
Wir können auch auf unserer Vorgänger(ancestors) zugreifen, die zur Wurzel des Dokuments gehen.
:host-context(.theme) { color: red; }
:host-context(#player1) { color: red; }
Das obige Beispiel ändert die color
nur, wenn die theme
Klasse auf eine der ancestors unserer Komponente angewendet wurde.
Das zweite Beispiel stimmt mit einem ancestor mit id:“player1“ überein.
###Entwerfen vom Host und Nachfolgern (Grenzübergänge)
Diese Option überschreibt alle Verkapselungseinstellungen einschließlich host children.
Dieser Selektor arbeitet für Shadow
und Light DOM
.
„Wir können Shadow DOM Grenzen mit / deep / überschreiben“
:host /deep/ .selected { color: red; }
:host >>> .selected { color: red; }
„Hinweis: benutze in Angular-CLI /deep/ anstelle von »>.“
Verwendung @Component.host
Durch die Verwendung dieser Eigenschaft können wir mit DOM properties
, `DOM attributes und events binden.
@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) : ...
}
})
Lass uns einige Beispiele mit class
und style Dom Attributen sehen.
@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 { }
Beachte die Verwendung von eckigen klammern, um eine Bindung zu erstellen.
Darum wird ‘true’ zu boolean true
.
Für die CSS-Eigenschaftsfarbe müssen wir einen String übergeben.
Bindung unsicherer Expressions
Um Missbrauch zu vermeiden, könnten einige Styling-Ausdrücke von Angular als unsicher markiert werden.
@Component({
host: {
'[style]': '_hostStyle' //unsafe
}
})
export class SongTrack { }
Wenn du vor diesem speziellen Problem stehst, kannst du die expression als sicher markieren, indem du den bypassSecurityTrustStyle
API auf dem Sanitizer
verwendest.
Dadurch werden viele Missbrauchs- oder Sicherheitsverletzungen vermieden.
export class SongTrack {
constructor(private sanitizer: Sanitizer){
this._hostStyle = this.sanitizer
.bypassSecurityTrustStyle('color: black;');
}
}
Benutzung @HostBinding
Wir können auch den @hostBinding
Dekorateur verwenden, um unsere Styles einzustellen.
Siehe einige Beispiele unten.
export class SongTrack {
//<host class="selected"></host>
@HostBinding('class.selected') selected = true;
//<host style="color: red;"></host>
@HostBinding('style.color') color = 'red';
}
@HostBinding Decorators werden in @Component.host
Metadaten übersetzt.
Benutzung von ElementRef und nativeElement APIs (Browser)
Manchmal wollen wir auf das zugrunde liegende DOM-Element zugreifen, um seine Styles zu verändern.
Um dies tun zu können, müssen wir ElementRef
einfügen, und auf die nativeElement
-Eigenschaft zugreifen.
Damit erhälst du Zugriff auf die DOM APIs.
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;");
}
}
Beachte, dass diese Option mit der Browser-Plattform funktioniert, aber nicht mit dem Desktop oder Handy.
Benutzung von Renderer und setElementClass/setElementStyle APIs (Web, Server, WebWorker)
Eine sichere Alternative zu ElementRef, um unser Styling einzusetzen, ist, den Renderer zusammen mit setElementClass
und setElementStyle
zu benutzen.
Ihre Implementierung wird die zugrunde liegende Plattform abstrahieren, welche die Kompatibilitätsbegrenzung von ElementRef überwindet.
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);
}
}
CSS Styles Spezifizierung und Ausführungsreihenfolge
Jegliches Styling folgt den Spezifitäts- und Bestellregeln.
Je spezifischer ein Style ist, desto höher ist die Priorität Mit der gleichen Spezifität überschreibt die letzte Style-Regel alle vorherigen Dies ist die Reihenfolge der Anwendung von Styles und ihrer Priorität von unten nach oben.
- Component implementation
- Styles defined at
@Component.styles
(following array order) - Template Inline Styles
- External styles @Component.styleUrls` (following array order)
- Styles defined at
- Container
- Inline style. Eg:
<... style="">
ngClass
andngStyle
- Inline style. Eg:
Wenn wir also ngStyle
verwenden, wird dies alle Inline-Styles überschreiben, die auf dem Element (und den vorherigen) definiert sind.
Styles werden statisch und dynamisch als Teil der Angular-Rendering-Ausführung und des Komponenten-Lebenszyklus angewendet.
Beachte, dass je nach Reihenfolge der Ausführung ein Style vom anderen überschrieben wird.
Zum Beispiel wird @Component.host
zuerst angewendet und dann @Hostbinding
.
Ich hoffe euch hat der Artikel geholfen, wenn ihr Anmerkungen oder Verbesserungen habt schreibt ein Kommentar oder stellt ein Pull-Request!
Inhaltsverzeichnis
Um alle Neuigkeiten zu erfahren, abonniere hier unseren Newsletter!
Newsletter abonnierenGerard Sans
Google Developer Expert | Coding is fun | Just be awesome | Blogger Speaker Trainer Community Leader | @angular_zone @graphql_london @ngcruise Made in London ☂