Für dynamische Klassen und Styles verwendest du die nativen Bindings [class] / [class.x] und [style] / [style.x]. Die Direktiven ngClass und ngStyle gibt es noch, aber der aktuelle Style Guide empfiehlt die nativen Bindings, und v21 hat automatische Migrationen dafür ergänzt.
Klassen
<!-- Eine einzelne bedingte Klasse -->
<li [class.active]="isActive()">...</li>
<!-- Mehrere Klassen: String, Array oder Objekt -->
<div [class]="'card elevated'"></div>
<div [class]="['card', size()]"></div>
<div [class]="{ card: true, selected: isSelected(), disabled: !enabled() }"></div>
<!-- Statische Klassen und die aus dem Binding werden KOMBINIERT, nicht überschrieben -->
<button class="btn" [class.btn-primary]="primary()">OK</button>Styles
<!-- Eine CSS-Eigenschaft, mit Einheit -->
<div [style.width.px]="width()"></div>
<div [style.height.%]="progress()"></div>
<div [style.font-size.rem]="scale()"></div>
<!-- CSS-Variablen (Custom Properties) -->
<div [style.--accent]="brandColor()"></div>
<!-- Mehrere Styles auf einmal: String oder Objekt -->
<div [style]="{ 'background-color': bg(), opacity: dim() ? 0.5 : 1 }"></div>Warum nicht ngClass / ngStyle
<!-- ✓ Neu: natives Binding, kein Import, schneller -->
<div [class]="{ active: isActive(), 'is-big': size() === 'lg' }"></div>
<div [style.width.px]="width()"></div><!-- ✗ Alt: braucht den Import von NgClass / NgStyle (oder CommonModule) -->
<div [ngClass]="{ active: isActive, 'is-big': size === 'lg' }"></div>
<div [ngStyle]="{ 'width.px': width }"></div>- Kein Import: Es ist Teil des Compilers, keine Direktive.
- Schneller: Es gibt keinen Differ, der bei jeder Change Detection das Objekt vergleicht.
- Besser lesbar im häufigsten Fall — einer bedingten Klasse:
[class.active]="x()".
# v21: automatische Migrationen
ng generate @angular/core:ngclass-to-class
ng generate @angular/core:ngstyle-to-styleKlassen auf dem Host-Element: host
Für das Host-Element einer Komponente (das Tag <app-button> selbst) nutzt du die Eigenschaft host der Metadaten, nicht @HostBinding. Ab v21 werden auch diese Bindings typgeprüft.
// Klassen auf dem Host-Element der Komponente → die `host`-Metadaten, nicht @HostBinding
@Component({
selector: 'app-button',
host: {
class: 'ui-btn', // statisch
'[class.is-loading]': 'loading()', // dynamisch
'[class]': 'variant()', // 'primary' | 'ghost'
'[style.--hue]': 'hue()',
'[attr.aria-busy]': 'loading()',
},
template: `<ng-content />`,
})
export class Button {
readonly variant = input<'primary' | 'ghost'>('primary');
readonly loading = input(false, { transform: booleanAttribute });
readonly hue = input(330);
}Wer gewinnt, wenn mehrere Stellen denselben Style setzen
Dasselbe Element kann einen Style von fünf Stellen bekommen: vom host der eigenen Komponente, vom host einer Direktive, von einem statischen style-Attribut, von einer [style]-Map und von einem [style.x]-Binding. Angular lässt nicht das zuletzt Geschriebene gewinnen — es wendet eine feste Rangfolge an.
- überschrieben
- überschrieben
- überschrieben
Der Gewinner wird aus dem Browser gelesen (getComputedStyle), nicht vom Lab berechnet.
Von oben: Ein Binding auf eine einzelne Property schlägt eine Map, eine Map schlägt einen statischen Wert — und alles im Template schlägt, was Direktiven und die Komponente auf ihren Host setzen. Sogar ein statisches style-Attribut im Template gewinnt gegen das Host-Binding einer Direktive. Für Klassen gilt dieselbe Reihenfolge: [class.active]="false" entfernt ein active, das im statischen class-Attribut steht. Noch ein Detail: Ein „ausgeschaltetes“ Binding sollte undefined sein — das überlässt die Entscheidung der Ebene darunter. null heißt „diesen Style entfernen“: Schalte darauf um, und der Hintergrund verschwindet, selbst wenn alle anderen Ebenen an sind.
// von der höchsten zur niedrigsten Priorität:
// 1. [style.background]="…" ein Property-Binding im Template
// 2. [style]="{ background: … }" ein Map-Binding im Template
// 3. style="background: …" ein statischer Wert im Template
// 4. Host-Bindings einer Direktive am Element
// 5. Host-Bindings der Komponente selbst
@Component({
template: `
<app-swatch
appTint
style="background: yellow"
[style]="{ background: 'green' }"
[style.background]="'red'"
/>
`,
})
export class Page {} // → Rot: Nummer 1 ist an
@Directive({ selector: '[appTint]', host: { '[style.background]': "'blue'" } })
export class Tint {}
@Component({ selector: 'app-swatch', host: { '[style.background]': "'gray'" }, template: '' })
export class Swatch {}Häufige Stolperfallen
[class]und[class.x]am selben Element funktionieren zusammen; die Prioritätsreihenfolge: Das spezifische Binding ([class.x]) gewinnt.- Das Klassen-Objekt mutieren: Wenn du dasselbe Objekt an Ort und Stelle änderst, bemerkt Angular das womöglich nicht. Erzeuge ein neues (oder nutze ein
computed()). - Vergessene Einheiten:
[style.width]="200"bewirkt nichts; schreibe[style.width.px]="200". - Gekapselte Styles: Klassen, die auf projizierte Kinder angewendet werden, werden vom CSS deiner Komponente nicht gestylt (nutze
:hostoder CSS-Variablen). Wie die Kapselung funktioniert, siehst du live unter Komponenten-Styles.