9 Property und Event Binding

9.1 Die Brücke zwischen Logik und Darstellung

Angular-Komponenten bestehen aus zwei fundamentalen Teilen: der TypeScript-Klasse, die Daten und Logik verwaltet, und dem HTML-Template, das die Benutzeroberfläche definiert. Binding ist der Mechanismus, der diese beiden Welten verbindet. Ohne Binding wären Komponenten statische Konstrukte ohne jede Interaktivität.

Das Binding-System von Angular unterscheidet sich grundlegend von klassischem JavaScript mit manueller DOM-Manipulation. Statt imperativer Befehle wie element.textContent = value oder element.addEventListener('click', handler) definiert Angular deklarativ im Template, wie Daten fließen und wie Ereignisse behandelt werden. Das Framework übernimmt die eigentliche Arbeit der DOM-Synchronisation.

Diese Abstraktion hat weitreichende Konsequenzen für die Entwicklung. Code wird lesbarer, wartbarer und weniger fehleranfällig. Angular’s Change Detection überwacht automatisch Änderungen und aktualisiert die Darstellung, ohne dass Entwickler jede DOM-Operation manuell implementieren müssen.

9.2 Die Change Detection als Fundament

Binding funktioniert nur, weil Angular kontinuierlich überwacht, wann sich Daten ändern. Dieser Mechanismus heißt Change Detection und läuft nach einem klaren Prinzip: Bei bestimmten Auslösern prüft Angular den gesamten Komponentenbaum auf Änderungen und synchronisiert das DOM entsprechend.

Die Auslöser für Change Detection sind vielfältig. Jede asynchrone Operation – HTTP-Requests, Timer, Promises – triggert einen Prüfzyklus. Benutzerinteraktionen wie Klicks, Tastatureingaben oder Mausbewegungen ebenfalls. Angular integriert sich dafür tief in die Browser-APIs und überwacht diese Ereignisse automatisch.

Der Ablauf ist klar definiert: Ein Ereignis tritt ein, Angular startet einen Change Detection-Zyklus, alle Komponenten werden geprüft, geänderte Bindungen werden aktualisiert, das DOM wird synchronisiert. Dieser Prozess ist hochoptimiert und läuft so schnell, dass Aktualisierungen für Benutzer nahezu instantan erscheinen.

9.3 Vier Kategorien des Datenaustauschs

Angular strukturiert Binding in vier Hauptkategorien, die jeweils unterschiedliche Datenflussrichtungen und Anwendungsfälle abdecken. Diese Kategorisierung ist nicht willkürlich, sondern spiegelt die natürlichen Kommunikationsmuster zwischen Komponente und Template wider.

Kategorie Syntax Datenfluss Primärer Zweck
Property Binding [property]="expression" Komponente → Template DOM-Eigenschaften setzen, Komponenteneingaben übertragen
Event Binding (event)="handler()" Template → Komponente Benutzerinteraktionen verarbeiten
Two-Way Binding [(ngModel)]="property" Komponente ↔︎ Template Bidirektionale Synchronisation, Formulareingaben
Attribute/Class/Style [attr.name]="value" Komponente → Template Spezielle DOM-Attribute manipulieren

Property Binding transportiert Daten von der Komponente ins Template. Event Binding macht das Gegenteil – es leitet Ereignisse vom Template zurück zur Komponente. Two-Way Binding vereint beide Richtungen in einer kompakten Syntax. Die spezialisierten Binding-Formen für Attribute, Klassen und Styles bieten feinkörnige Kontrolle über DOM-Eigenschaften.

9.4 Property Binding im Detail

Die Syntax mit eckigen Klammern signalisiert Property Binding. Angular wertet den Ausdruck rechts aus und weist das Ergebnis der Eigenschaft links zu. Diese Zuweisung ist reaktiv – ändert sich der Wert in der Komponente, aktualisiert Angular automatisch die DOM-Eigenschaft.

Ein kritisches Verständnis betrifft den Unterschied zwischen HTML-Attributen und DOM-Eigenschaften. HTML-Attribute sind statische Werte im Markup, die die initiale Konfiguration definieren. DOM-Eigenschaften sind dynamische JavaScript-Eigenschaften des Element-Objekts, die sich zur Laufzeit ändern können.

<!-- HTML-Attribut: statisch im Markup -->
<input value="Anfangswert">

<!-- Property Binding: dynamisch aktualisiert -->
<input [value]="currentValue">

Wenn ein Benutzer Text in ein Input-Feld tippt, ändert sich die DOM-Eigenschaft value, das HTML-Attribut value bleibt jedoch unverändert. Angular bindet an DOM-Eigenschaften, nicht an Attribute – mit einer Ausnahme, dem speziellen Attribute-Binding, das später behandelt wird.

Ein einfaches Beispiel demonstriert die Grundlagen:

export class ImageComponent {
  imageUrl = 'assets/logo.png';
  imageAlt = 'Firmenlogo';
  isButtonDisabled = false;
  
  toggleButton() {
    this.isButtonDisabled = !this.isButtonDisabled;
  }
}

Im Template:

<img [src]="imageUrl" [alt]="imageAlt">
<button [disabled]="isButtonDisabled" (click)="toggleButton()">
  Aktion ausführen
</button>

Angular wertet imageUrl aus und setzt die src-Eigenschaft des img-Elements. Bei jedem Click wird isButtonDisabled umgeschaltet, und der Button reagiert sofort durch Aktivierung oder Deaktivierung.

9.5 Interpolation versus Property Binding

Angular bietet zwei Wege, Werte ans Template zu binden: Interpolation mit doppelten geschwungenen Klammern und Property Binding mit eckigen Klammern. Beide Mechanismen führen letztlich zum gleichen Ergebnis, unterscheiden sich aber in Lesbarkeit und Anwendungsfall.

<!-- Interpolation: intuitiv für Text -->
<p>Willkommen, {{ userName }}!</p>

<!-- Property Binding: explizit für Eigenschaften -->
<p [textContent]="'Willkommen, ' + userName + '!'"></p>

Interpolation eignet sich für Textinhalte und String-Ausgaben. Die Syntax ist kompakt und die Intention klar. Property Binding zeigt seine Stärken bei Nicht-String-Werten wie Zahlen, Booleans oder Objekten. Hier würde Interpolation zu impliziter String-Konvertierung führen, während Property Binding die Typen erhält.

<!-- Interpolation konvertiert zu String -->
<div>Zählerstand: {{ counter }}</div>

<!-- Property Binding erhält den Typ -->
<progress [value]="counter" [max]="maxValue"></progress>

Das progress-Element erwartet numerische Werte. Property Binding übergibt sie direkt, ohne String-Konvertierung. Dies ist nicht nur semantisch korrekter, sondern auch performanter.

9.6 Native DOM-Eigenschaften binden

Angular ermöglicht direkten Zugriff auf praktisch jede DOM-Eigenschaft. Dies ist besonders mächtig bei komplexen Elementen wie Media-Playern, wo viele Eigenschaften zur Laufzeit gesteuert werden müssen.

<video
  [src]="videoSource"
  [currentTime]="startTime"
  [playbackRate]="playSpeed"
  [muted]="isMuted"
  (timeupdate)="updateProgress($event)">
</video>

Die Komponente steuert jeden Aspekt des Video-Elements:

export class MediaPlayerComponent {
  videoSource = 'assets/demo.mp4';
  startTime = 30;      // Video startet bei 30 Sekunden
  playSpeed = 1.5;     // 1.5-fache Geschwindigkeit
  isMuted = false;
  currentProgress = 0;
  
  updateProgress(event: Event) {
    const video = event.target as HTMLVideoElement;
    this.currentProgress = (video.currentTime / video.duration) * 100;
    
    // Kapitelmarkierungen bei bestimmten Zeitpunkten
    if (video.currentTime >= 45 && video.currentTime <= 46) {
      this.showChapterIndicator('Kapitel 2');
    }
  }
  
  setPlaybackSpeed(speed: number) {
    this.playSpeed = speed;
  }
  
  private showChapterIndicator(chapter: string) {
    console.log(`Jetzt läuft: ${chapter}`);
  }
}

Die currentTime-Eigenschaft springt zu einer bestimmten Stelle, playbackRate ändert die Geschwindigkeit, muted kontrolliert den Ton. Alle Änderungen wirken sofort, ohne manuelle DOM-Manipulation.

Ein weiteres Beispiel zeigt die Steuerung von Text-Selektion:

<input 
  [value]="initialValue" 
  [selectionStart]="5" 
  [selectionEnd]="10"
  (focus)="logFocus()">

Beim Fokussieren markiert Angular automatisch die Zeichen 5 bis 10 im Input-Feld. Diese programmatische Kontrolle über Selektion ist mit reinem HTML nicht möglich.

9.7 Sicherheit durch Sanitization

Angular’s Sicherheitsmodell schützt vor Cross-Site-Scripting (XSS) durch automatische Sanitization. Besonders beim Binding von HTML-Inhalten ist dies essentiell, da unsanitisierter Input potentiell schädlichen Code ausführen könnte.

<div [innerHTML]="htmlContent"></div>

Angular sanitisiert htmlContent automatisch. Potentiell gefährliche Elemente wie <script>-Tags oder Event-Handler werden entfernt. Dies geschieht kontextabhängig – für HTML-Inhalte gelten andere Regeln als für URLs oder CSS-Styles.

Der DomSanitizer-Service erlaubt explizite Kontrolle über diesen Prozess:

import { Component } from '@angular/core';
import { DomSanitizer, SafeHtml, SafeResourceUrl } from '@angular/platform-browser';

@Component({
  selector: 'app-security-demo',
  template: `
    <div [innerHTML]="safeHtml"></div>
    <iframe [src]="safeVideoUrl" width="560" height="315"></iframe>
  `
})
export class SecurityDemoComponent {
  rawHtml = '<div onclick="alert(\'XSS?\')">Klick mich</div>';
  videoUrl = 'https://www.youtube.com/embed/dQw4w9WgXcQ';
  
  safeHtml: SafeHtml;
  safeVideoUrl: SafeResourceUrl;
  
  constructor(private sanitizer: DomSanitizer) {
    this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(this.rawHtml);
    this.safeVideoUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.videoUrl);
  }
}

Die bypassSecurityTrust*-Methoden umgehen die Sanitization explizit. Dies sollte ausschließlich für vertrauenswürdige Inhalte verwendet werden – niemals für Benutzereingaben ohne vorherige Validierung. Der Methodenname ist bewusst ausführlich, um die Ernsthaftigkeit dieser Entscheidung zu verdeutlichen.

Angular unterscheidet verschiedene Sicherheitskontexte:

Jeder Kontext hat spezifische Sicherheitsrisiken. Ein URL-Kontext erlaubt beispielsweise keine javascript:-URLs, während ein Ressourcen-Kontext externe Inhalte ohne Einschränkungen lädt.

9.8 Event Binding und Benutzerinteraktion

Event Binding fängt Ereignisse aus dem Template ab und leitet sie an Methoden in der Komponente. Die Syntax mit runden Klammern signalisiert diesen Datenfluss vom Template zur Komponente.

<button (click)="handleClick($event)">Klick mich</button>

Angular registriert einen Event-Listener für das click-Ereignis. Bei jedem Klick ruft das Framework die handleClick-Methode auf und übergibt das native DOM-Event als $event-Objekt.

Das Framework unterstützt alle nativen DOM-Ereignisse: Mausereignisse wie click, dblclick, mousedown, mouseup, Tastaturereignisse wie keydown, keyup, Formulareeignisse wie submit, change, input, focus, blur, sowie spezialisierte Ereignisse für Drag & Drop und Touch-Interaktionen.

<input 
  (input)="onInput($event)" 
  (focus)="highlightField(true)" 
  (blur)="highlightField(false)"
  (keyup.enter)="submitForm()">

Mehrere Event-Bindings auf einem Element sind problemlos möglich. Jedes Ereignis triggert seine eigene Handler-Methode. Die Komponente verarbeitet die Eingabe:

export class FormFieldComponent {
  fieldValue = '';
  isHighlighted = false;
  
  onInput(event: Event) {
    const input = event.target as HTMLInputElement;
    this.fieldValue = input.value;
    
    // Echtzeit-Validierung
    if (this.fieldValue.length < 3) {
      input.classList.add('invalid');
    } else {
      input.classList.remove('invalid');
    }
  }
  
  highlightField(isActive: boolean) {
    this.isHighlighted = isActive;
  }
  
  submitForm() {
    console.log('Formular abgesendet mit:', this.fieldValue);
  }
}

Die onInput-Methode reagiert auf jede Eingabe, führt Validierung durch und aktualisiert CSS-Klassen. Dies zeigt die Flexibilität des Event-Systems – vom einfachen Event-Handling bis zur komplexen Interaktionslogik.

9.9 Das $event-Objekt analysieren

Das $event-Objekt enthält alle Informationen über das ausgelöste Ereignis. TypeScript bietet typisierte Event-Interfaces, die spezifische Eigenschaften für verschiedene Event-Typen bereitstellen.

export class EventAnalysisComponent {
  handleMouseEvent(event: MouseEvent) {
    console.log(`Mausposition: ${event.clientX}, ${event.clientY}`);
    
    // Welcher Mausbutton wurde geklickt?
    switch(event.button) {
      case 0: console.log('Linksklick'); break;
      case 1: console.log('Mittelklick/Scroll-Wheel'); break;
      case 2: console.log('Rechtsklick'); break;
    }
    
    // Relative Position zum Zielelement berechnen
    const target = event.target as HTMLElement;
    const rect = target.getBoundingClientRect();
    const relativeX = event.clientX - rect.left;
    const relativeY = event.clientY - rect.top;
    console.log(`Relative Position: ${relativeX}, ${relativeY}`);
    
    // Modifier-Tasten prüfen
    if (event.ctrlKey) console.log('Strg gedrückt');
    if (event.shiftKey) console.log('Shift gedrückt');
    if (event.altKey) console.log('Alt gedrückt');
  }
  
  handleKeyboardEvent(event: KeyboardEvent) {
    console.log(`Taste: ${event.key}, Code: ${event.code}`);
    
    // Unterschied zwischen key und code:
    // key: logische Eingabe (z.B. "a" oder "A" je nach Shift)
    // code: physische Taste (immer "KeyA")
    
    if (event.code === 'KeyA' && event.key !== 'a') {
      console.log('A-Taste mit alternativer Belegung');
    }
  }
  
  handleTouchEvent(event: TouchEvent) {
    const touches = event.touches;
    console.log(`Aktive Touch-Points: ${touches.length}`);
    
    // Multi-Touch-Gesten erkennen
    if (touches.length >= 2) {
      const touch1 = touches[0];
      const touch2 = touches[1];
      
      const distance = Math.sqrt(
        Math.pow(touch2.clientX - touch1.clientX, 2) +
        Math.pow(touch2.clientY - touch1.clientY, 2)
      );
      
      console.log(`Abstand zwischen Fingern: ${distance}px`);
    }
  }
}

TypeScript’s Event-Typen bieten Intellisense und Compile-Zeit-Sicherheit. Ein MouseEvent hat andere Eigenschaften als ein KeyboardEvent oder TouchEvent. Die IDE kann auto-complete für alle verfügbaren Eigenschaften anbieten.

9.10 Event-Modifikatoren vereinfachen Code

Angular bietet Syntactic Sugar für häufige Event-Kombinationen. Statt im Handler manuell zu prüfen, welche Tasten gedrückt wurden, deklariert die Template-Syntax dies direkt:

<!-- Speichern mit Strg+S -->
<textarea (keydown.control.s)="saveDocument($event)"></textarea>

<!-- Navigation mit Shift+Pfeiltasten -->
<div 
  (keydown.shift.arrowUp)="moveSelection('up')"
  (keydown.shift.arrowDown)="moveSelection('down')"
  tabindex="0">
  Tastatur-navigierbarer Inhalt
</div>

Die Komponente muss nicht mehr prüfen, ob Modifier-Tasten gedrückt sind:

export class DocumentEditorComponent {
  saveDocument(event: KeyboardEvent) {
    event.preventDefault(); // Verhindert Browser-Speicherdialog
    
    this.documentService.save(this.currentDocument);
    this.showNotification('Dokument gespeichert');
  }
  
  moveSelection(direction: 'up' | 'down') {
    // Keine manuelle Prüfung von event.shiftKey nötig
    console.log(`Auswahl nach ${direction} erweitern`);
  }
}

Verfügbare Modifikatoren umfassen Tastaturmodifikatoren (control, shift, alt, meta), spezifische Tasten (enter, tab, escape, space, backspace, delete), Pfeiltasten (arrowUp, arrowDown, arrowLeft, arrowRight) und Kombinationen für Mausereignisse.

Diese Modifikatoren können beliebig kombiniert werden: (keydown.control.shift.a) fängt Strg+Shift+A ab. Die Syntax ist deklarativ und selbstdokumentierend.

9.11 Event-Bubbling und Propagation

DOM-Events durchlaufen zwei Phasen: Die Capture-Phase, in der das Event vom Window zum Zielelement wandert, und die Bubbling-Phase, in der es wieder aufsteigt. Angular bindet standardmäßig an die Bubbling-Phase.

<div (click)="handleOuter($event)" class="outer">
  Äußeres Element
  <button (click)="handleInner($event)" class="inner">
    Inneres Element
  </button>
</div>

Ein Klick auf den Button triggert beide Handler in dieser Reihenfolge: erst handleInner, dann handleOuter. Das Event “bubbelt” durch die DOM-Hierarchie nach oben.

export class BubblingDemoComponent {
  handleInner(event: Event) {
    console.log('Button geklickt');
    event.stopPropagation(); // Stoppt das Aufsteigen
  }
  
  handleOuter(event: Event) {
    console.log('Äußeres Div geklickt');
    // Wird nicht ausgeführt, wenn stopPropagation() aufgerufen wurde
  }
}

Die Methode stopPropagation() verhindert, dass das Event weiter aufsteigt. Dies ist nützlich, wenn nested Elements unterschiedliche Aktionen ausführen sollen.

Für spezielle Fälle unterstützt Angular auch Capture-Phase-Binding:

<div (click.capture)="handleCapture($event)">
  Capture-Phase
  <button (click)="handleBubble($event)">
    Bubbling-Phase
  </button>
</div>

Die Capture-Phase läuft vor der Bubbling-Phase. Bei einem Button-Klick wird zuerst handleCapture, dann handleBubble ausgeführt. Dies ist selten nötig, kann aber für Event-Interception oder komplexe Delegationsmuster hilfreich sein.

9.12 Event-Delegation für Performance

Bei langen Listen kann das Hinzufügen eines Event-Handlers zu jedem Element die Performance beeinträchtigen. Event-Delegation löst dies durch einen einzelnen Handler auf einem gemeinsamen Elternelement:

<ul (click)="handleListClick($event)">
  <li *ngFor="let item of items" [attr.data-id]="item.id">
    {{ item.name }}
  </li>
</ul>

Der Handler auf dem <ul> fängt alle Klicks ab und delegiert basierend auf dem Zielelement:

export class EfficientListComponent {
  items = Array.from({length: 1000}, (_, i) => ({
    id: i,
    name: `Item ${i}`
  }));
  
  handleListClick(event: MouseEvent) {
    let target = event.target as HTMLElement;
    
    // Finde das nächste <li>-Element in der Hierarchie
    while (target && target.tagName !== 'LI') {
      target = target.parentElement as HTMLElement;
    }
    
    if (target) {
      const itemId = target.getAttribute('data-id');
      this.selectItem(Number(itemId));
    }
  }
  
  selectItem(id: number) {
    console.log(`Item ${id} ausgewählt`);
  }
}

Statt 1000 Event-Listener zu registrieren, genügt einer. Dies reduziert Memory-Overhead und verbessert Initial-Rendering. Die Technik funktioniert auch für dynamisch hinzugefügte Elemente, da der Handler bereits auf dem Elternelement existiert.

9.13 Two-Way Binding mit ngModel

Two-Way Binding vereint Property Binding und Event Binding in einer kompakten Syntax. Die Notation [()] wird oft als “Banana in a Box” bezeichnet – die runden Klammern (Banana) stecken in den eckigen (Box).

<input [(ngModel)]="username">

Angular expandiert dies intern zu:

<input 
  [ngModel]="username" 
  (ngModelChange)="username = $event">

Das Property Binding [ngModel] setzt den Input-Wert, das Event Binding (ngModelChange) aktualisiert die Komponenten-Eigenschaft bei Änderungen. Die Schreibweise [(ngModel)] ist syntaktischer Zucker, der beide Mechanismen kombiniert.

Die Verwendung erfordert den Import des FormsModule:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ]
})
export class AppModule { }

Ein Formular demonstriert die praktische Anwendung:

@Component({
  selector: 'app-user-form',
  template: `
    <div class="form-group">
      <label for="username">Benutzername:</label>
      <input id="username" type="text" [(ngModel)]="username">
    </div>
    
    <div class="form-group">
      <label for="email">E-Mail:</label>
      <input id="email" type="email" [(ngModel)]="email">
    </div>
    
    <div class="preview">
      <p>Benutzername: {{ username }}</p>
      <p>E-Mail: {{ email }}</p>
    </div>
  `
})
export class UserFormComponent {
  username = '';
  email = '';
}

Jede Änderung im Input-Feld aktualisiert sofort die Komponenten-Eigenschaft und damit die Preview. Diese bidirektionale Synchronisation eliminiert Boilerplate-Code für Formulare.

9.14 Eigene Two-Way Binding Komponenten

Two-Way Binding ist nicht auf ngModel beschränkt. Eigene Komponenten können das gleiche Pattern implementieren. Die Konvention erfordert eine Input-Eigenschaft und ein Output-Event mit dem Suffix Change:

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

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="decrement()">-</button>
    <span>{{ value }}</span>
    <button (click)="increment()">+</button>
  `
})
export class CounterComponent {
  @Input() value: number = 0;
  @Output() valueChange = new EventEmitter<number>();
  
  increment() {
    this.value++;
    this.valueChange.emit(this.value);
  }
  
  decrement() {
    this.value--;
    this.valueChange.emit(this.value);
  }
}

Die Verwendung unterstützt sowohl separate Bindings als auch die Two-Way-Syntax:

<!-- Separate Bindings -->
<app-counter 
  [value]="count" 
  (valueChange)="count = $event">
</app-counter>

<!-- Two-Way Binding -->
<app-counter [(value)]="count"></app-counter>

Angular erkennt das Pattern automatisch. Wenn eine Eigenschaft foo und ein Event fooChange existieren, funktioniert [(foo)] out-of-the-box. Diese Konvention macht eigene Komponenten intuitiv verwendbar.

9.15 Attribute, Class und Style Binding

Neben dem generellen Property Binding bietet Angular spezialisierte Syntax für Attribute, CSS-Klassen und Styles. Diese Binding-Formen adressieren spezifische DOM-Aspekte, die über reguläres Property Binding umständlich wären.

9.15.1 Attribute Binding

HTML-Attribute ohne entsprechende DOM-Eigenschaften erfordern Attribute Binding:

<button [attr.aria-label]="description">Aktion</button>
<table [attr.role]="'grid'">...</table>
<td [attr.colspan]="columnSpan">Zelle</td>

Das attr.-Präfix signalisiert Attribute-Binding. Angular setzt das HTML-Attribut direkt, nicht eine DOM-Eigenschaft. Dies ist essentiell für ARIA-Attribute, SVG-Eigenschaften und Custom Data-Attributes:

<div 
  [attr.data-user-id]="userId"
  [attr.data-role]="userRole">
  Benutzer-Karte
</div>

9.15.2 Class Binding

CSS-Klassen können über mehrere Wege gebunden werden:

<!-- Einzelne Klasse toggeln -->
<div [class.active]="isActive">Status</div>

<!-- Mehrere Klassen als String -->
<div [class]="'btn btn-primary'">Button</div>

<!-- Klassen-Objekt -->
<div [class]="classObject">Multi-Class</div>

Die Komponente definiert das Klassen-Objekt:

export class StyleComponent {
  isActive = true;
  
  classObject = {
    'active': this.isActive,
    'highlighted': true,
    'disabled': false
  };
}

Angular wendet nur die Klassen an, deren Wert true ist. Diese Syntax ist prägnanter als String-Konkatenation, besonders bei vielen bedingten Klassen.

9.15.3 Style Binding

Inline-Styles werden ähnlich gebunden:

<!-- Einzelner Style -->
<div [style.color]="textColor">Text</div>

<!-- Mit Einheit -->
<div [style.width.px]="elementWidth">Element</div>

<!-- Style-Objekt -->
<div [style]="styleObject">Styled</div>

Die Komponente:

export class StyleComponent {
  textColor = '#ff0000';
  elementWidth = 200;
  
  styleObject = {
    'color': this.textColor,
    'font-size': '16px',
    'font-weight': 'bold'
  };
}

Das .px-Suffix fügt automatisch die Einheit hinzu. Angular unterstützt auch andere Einheiten wie .em, .%, .rem. Dies eliminiert String-Konkatenation für Style-Werte mit Einheiten.

9.16 Direktiven als Binding-Targets

Property und Event Binding funktioniert nicht nur mit DOM-Elementen, sondern auch mit Direktiven. Angular’s eingebaute Direktiven nutzen dies extensiv:

<div *ngIf="isVisible">Sichtbarer Inhalt</div>
<div *ngFor="let item of items">{{ item }}</div>
<div [ngClass]="classExpression">Dynamische Klassen</div>
<div [ngStyle]="styleExpression">Dynamische Styles</div>

Eigene Direktiven können ebenfalls Input-Properties definieren:

import { Directive, Input, ElementRef, OnInit } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective implements OnInit {
  @Input() appHighlight: string;
  @Input() defaultColor = 'yellow';
  
  constructor(private el: ElementRef) {}
  
  ngOnInit() {
    const color = this.appHighlight || this.defaultColor;
    this.el.nativeElement.style.backgroundColor = color;
  }
}

Die Verwendung im Template:

<p appHighlight="lightblue">Blau hervorgehobener Text</p>
<p [appHighlight]="highlightColor">Dynamisch hervorgehobener Text</p>

Direktiven erweitern DOM-Elemente um zusätzliches Verhalten, ohne neue Komponenten zu erstellen. Property Binding macht sie konfigurierbar, Event Binding ermöglicht Kommunikation zurück zur Komponente.

Die Kombination aus Komponenten, Direktiven und den verschiedenen Binding-Mechanismen bildet das Fundament für Angular’s deklaratives, kompositionsfähiges UI-Modell. Binding abstrahiert die imperative DOM-Manipulation und erlaubt es Entwicklern, sich auf die Geschäftslogik zu konzentrieren statt auf technische Details der Browser-APIs.