Anfänger202025-01-31

Angular Directives - DOM dynamisch steuern

Meistere Angular Directives: *ngIf für Conditional Rendering, *ngFor für Listen, ngClass/ngStyle für Styling und eigene Custom Directives.

#angular#directives#ngIf#ngFor#ngClass#ngStyle#structural-directives

Angular Directives - DOM dynamisch steuern

Directives geben dir superpowers um das DOM zu manipulieren! Lerne die wichtigsten Built-in Directives und erstelle eigene.

📋 In diesem Artikel lernst du:

  • Was Directives sind und die 3 Typen
  • *ngIf - Conditional Rendering
  • *ngFor - Listen rendern
  • *ngSwitch - Multiple Conditions
  • ngClass & ngStyle - Dynamisches Styling
  • Custom Directives erstellen
  • Best Practices für Directives

Was sind Directives?

Directives sind Anweisungen an Angular, wie das DOM verändert werden soll.

Denke an Directives wie Zauberstäbe: 🪄

  • Sie verändern das Aussehen oder Verhalten von Elementen
  • Manche fügen Elemente hinzu/entfernen (*ngIf, *ngFor)
  • Andere ändern nur Eigenschaften (ngClass, ngStyle)

Die 3 Typen von Directives

  1. Component Directives

    • Components sind eigentlich Directives mit Template!
    • <app-user-profile> ist eine Component Directive
  2. Structural Directives (mit *)

    • Ändern die DOM-Struktur
    • Fügen Elemente hinzu/entfernen
    • *ngIf, *ngFor, *ngSwitch
  3. Attribute Directives

    • Ändern Aussehen/Verhalten
    • Keine Struktur-Änderung
    • ngClass, ngStyle, ngModel

*ngIf - Conditional Rendering

Zeige/verstecke Elemente basierend auf Bedingungen.

Basic ngIf

export class UserComponent {
  isLoggedIn = true;
  userName = 'Max';
  age = 25;
}
<!-- Zeige nur wenn isLoggedIn true ist -->
<div *ngIf="isLoggedIn">
  <h2>Willkommen zurück, {{ userName }}!</h2>
</div>

<!-- Mit Bedingung -->
<p *ngIf="age >= 18">Du bist volljährig</p>
<p *ngIf="age < 18">Du bist minderjährig</p>

Wichtig: Das Element wird aus dem DOM entfernt, nicht nur versteckt!

ngIf mit else

<div *ngIf="isLoggedIn; else loggedOut">
  <h2>Willkommen, {{ userName }}!</h2>
</div>

<ng-template #loggedOut>
  <h2>Bitte melde dich an</h2>
</ng-template>

Erklärung:

  • ;else loggedOut - Wenn false, zeige Template "loggedOut"
  • <ng-template #loggedOut> - Template-Definition

ngIf mit then und else

<div *ngIf="isLoggedIn; then loggedIn; else loggedOut"></div>

<ng-template #loggedIn>
  <h2>Willkommen zurück!</h2>
</ng-template>

<ng-template #loggedOut>
  <h2>Bitte anmelden</h2>
</ng-template>

ngIf mit Variable (as)

user$ = {
  name: 'Max',
  email: 'max@example.com',
  role: 'admin'
};
<div *ngIf="user$ as user">
  <h2>{{ user.name }}</h2>
  <p>{{ user.email }}</p>
  <span *ngIf="user.role === 'admin'">Admin-Badge</span>
</div>

Komplexe Bedingungen

<!-- AND -->
<div *ngIf="isLoggedIn && isAdmin">
  Admin Panel
</div>

<!-- OR -->
<div *ngIf="isLoading || hasError">
  Bitte warten...
</div>

<!-- Vergleiche -->
<div *ngIf="age >= 18 && age <= 65">
  Erwerbsfähig
</div>

<!-- Funktionen -->
<div *ngIf="checkPermission('write')">
  Bearbeiten erlaubt
</div>

*ngFor - Listen rendern

Iteriere über Arrays und erstelle Elemente für jedes Item.

Basic ngFor

export class UserListComponent {
  users = [
    { id: 1, name: 'Max', age: 25 },
    { id: 2, name: 'Anna', age: 30 },
    { id: 3, name: 'Tom', age: 22 }
  ];
}
<ul>
  <li *ngFor="let user of users">
    {{ user.name }} ({{ user.age }} Jahre)
  </li>
</ul>

Ausgabe:

• Max (25 Jahre)
• Anna (30 Jahre)
• Tom (22 Jahre)

ngFor mit Index

<ul>
  <li *ngFor="let user of users; let i = index">
    {{ i + 1 }}. {{ user.name }}
  </li>
</ul>

Ausgabe:

1. Max
2. Anna
3. Tom

ngFor mit trackBy (Performance!)

export class UserListComponent {
  users = [
    { id: 1, name: 'Max', age: 25 },
    { id: 2, name: 'Anna', age: 30 },
    { id: 3, name: 'Tom', age: 22 }
  ];

  // TrackBy Funktion
  trackByUserId(index: number, user: any): number {
    return user.id;  // Nutze eindeutige ID!
  }
}
<ul>
  <li *ngFor="let user of users; trackBy: trackByUserId">
    {{ user.name }}
  </li>
</ul>

Warum trackBy?

  • Angular weiß welches Element welches ist
  • Nur geänderte Elemente werden neu gerendert
  • Viel bessere Performance bei großen Listen!

ngFor Spezial-Variablen

<ul>
  <li *ngFor="let user of users;
              let i = index;
              let first = first;
              let last = last;
              let even = even;
              let odd = odd">

    <span [class.highlight]="first">
      {{ i }}. {{ user.name }}
    </span>

    <span *ngIf="first">(Erster)</span>
    <span *ngIf="last">(Letzter)</span>
    <span *ngIf="even">(Gerade Zeile)</span>
    <span *ngIf="odd">(Ungerade Zeile)</span>
  </li>
</ul>

Verschachtelte ngFor

categories = [
  {
    name: 'Obst',
    items: ['Apfel', 'Birne', 'Orange']
  },
  {
    name: 'Gemüse',
    items: ['Karotte', 'Tomate', 'Gurke']
  }
];
<div *ngFor="let category of categories">
  <h3>{{ category.name }}</h3>
  <ul>
    <li *ngFor="let item of category.items">
      {{ item }}
    </li>
  </ul>
</div>

Leere Liste behandeln

<div *ngIf="users.length > 0; else noUsers">
  <ul>
    <li *ngFor="let user of users">
      {{ user.name }}
    </li>
  </ul>
</div>

<ng-template #noUsers>
  <p>Keine Benutzer gefunden.</p>
</ng-template>

*ngSwitch - Multiple Conditions

Wie switch-case in TypeScript, aber im Template.

export class StatusComponent {
  status = 'loading'; // 'loading', 'success', 'error'
}
<div [ngSwitch]="status">
  <p *ngSwitchCase="'loading'">
    Lädt...
  </p>

  <p *ngSwitchCase="'success'">
    ✓ Erfolgreich geladen!
  </p>

  <p *ngSwitchCase="'error'">
    ✗ Fehler beim Laden
  </p>

  <p *ngSwitchDefault>
    Unbekannter Status
  </p>
</div>

ngSwitch Beispiel: User-Rolle

userRole = 'admin'; // 'admin', 'editor', 'viewer'
<div [ngSwitch]="userRole">
  <div *ngSwitchCase="'admin'">
    <h2>Admin Dashboard</h2>
    <button>Benutzer verwalten</button>
    <button>Einstellungen</button>
  </div>

  <div *ngSwitchCase="'editor'">
    <h2>Editor Dashboard</h2>
    <button>Artikel bearbeiten</button>
  </div>

  <div *ngSwitchCase="'viewer'">
    <h2>Viewer Dashboard</h2>
    <p>Nur Lesezugriff</p>
  </div>

  <div *ngSwitchDefault>
    <p>Keine Berechtigung</p>
  </div>
</div>

ngClass - Dynamische CSS-Klassen

Füge/entferne CSS-Klassen basierend auf Bedingungen.

Object Syntax

export class ButtonComponent {
  isActive = true;
  isDisabled = false;
  isPrimary = true;
}
<button [ngClass]="{
  'active': isActive,
  'disabled': isDisabled,
  'btn-primary': isPrimary,
  'btn-secondary': !isPrimary
}">
  Click me
</button>

Resultat wenn true:

<button class="active btn-primary">Click me</button>

Array Syntax

buttonClasses = ['btn', 'btn-large', 'btn-primary'];
<button [ngClass]="buttonClasses">
  Click me
</button>

String Syntax

classList = 'btn btn-primary active';
<button [ngClass]="classList">
  Click me
</button>

Method Syntax

getButtonClasses() {
  return {
    'btn-success': this.isSuccess,
    'btn-danger': this.isError,
    'btn-lg': this.isLarge
  };
}
<button [ngClass]="getButtonClasses()">
  Click me
</button>

Praktisches Beispiel: Todo Item

todo = {
  text: 'Angular lernen',
  done: true,
  important: false
};
<div [ngClass]="{
  'todo-item': true,
  'done': todo.done,
  'important': todo.important,
  'pending': !todo.done
}">
  {{ todo.text }}
</div>
.todo-item { padding: 10px; margin: 5px; }
.todo-item.done {
  text-decoration: line-through;
  opacity: 0.6;
}
.todo-item.important {
  border-left: 4px solid red;
}
.todo-item.pending {
  font-weight: bold;
}

ngStyle - Dynamische Inline-Styles

Setze CSS-Styles dynamisch.

Object Syntax

export class BoxComponent {
  backgroundColor = '#3498db';
  textColor = '#ffffff';
  fontSize = 16;
  padding = 20;
}
<div [ngStyle]="{
  'background-color': backgroundColor,
  'color': textColor,
  'font-size.px': fontSize,
  'padding.px': padding
}">
  Styled Box
</div>

Method Syntax

getStyles() {
  return {
    'width': this.width + 'px',
    'height': this.height + 'px',
    'background-color': this.getColor(),
    'border-radius': this.rounded ? '8px' : '0'
  };
}

getColor() {
  return this.isDanger ? 'red' : 'green';
}
<div [ngStyle]="getStyles()">
  Dynamic Styles
</div>

Praktisches Beispiel: Progress Bar

export class ProgressBarComponent {
  progress = 75; // 0-100

  getProgressStyles() {
    return {
      'width': this.progress + '%',
      'background-color': this.getProgressColor(),
      'transition': 'width 0.3s ease'
    };
  }

  getProgressColor() {
    if (this.progress < 33) return '#e74c3c'; // Rot
    if (this.progress < 66) return '#f39c12'; // Orange
    return '#27ae60'; // Grün
  }
}
<div class="progress-container">
  <div class="progress-bar" [ngStyle]="getProgressStyles()">
    {{ progress }}%
  </div>
</div>
.progress-container {
  width: 100%;
  height: 30px;
  background-color: #ecf0f1;
  border-radius: 15px;
  overflow: hidden;
}

.progress-bar {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-weight: bold;
}

Vollständiges Beispiel: User-Liste mit Filters

export class UserListComponent {
  users = [
    { id: 1, name: 'Max', age: 25, status: 'active', role: 'admin' },
    { id: 2, name: 'Anna', age: 30, status: 'active', role: 'editor' },
    { id: 3, name: 'Tom', age: 22, status: 'inactive', role: 'viewer' },
    { id: 4, name: 'Lisa', age: 28, status: 'active', role: 'editor' }
  ];

  filterStatus = 'all'; // 'all', 'active', 'inactive'
  showDetails = false;

  getFilteredUsers() {
    if (this.filterStatus === 'all') {
      return this.users;
    }
    return this.users.filter(u => u.status === this.filterStatus);
  }

  getRoleBadgeClass(role: string) {
    return {
      'badge': true,
      'badge-admin': role === 'admin',
      'badge-editor': role === 'editor',
      'badge-viewer': role === 'viewer'
    };
  }

  trackByUserId(index: number, user: any) {
    return user.id;
  }
}
<div class="user-list">
  <h2>Benutzerverwaltung</h2>

  <!-- Filter Buttons -->
  <div class="filters">
    <button
      (click)="filterStatus = 'all'"
      [ngClass]="{'active': filterStatus === 'all'}">
      Alle
    </button>

    <button
      (click)="filterStatus = 'active'"
      [ngClass]="{'active': filterStatus === 'active'}">
      Aktiv
    </button>

    <button
      (click)="filterStatus = 'inactive'"
      [ngClass]="{'active': filterStatus === 'inactive'}">
      Inaktiv
    </button>
  </div>

  <!-- User Liste -->
  <div *ngIf="getFilteredUsers().length > 0; else noUsers">
    <div
      *ngFor="let user of getFilteredUsers();
              trackBy: trackByUserId;
              let i = index;
              let odd = odd"
      class="user-card"
      [ngClass]="{'odd': odd, 'inactive': user.status === 'inactive'}">

      <div class="user-header">
        <h3>{{ i + 1 }}. {{ user.name }}</h3>
        <span [ngClass]="getRoleBadgeClass(user.role)">
          {{ user.role }}
        </span>
      </div>

      <div class="user-info">
        <p>Alter: {{ user.age }}</p>
        <p>
          Status:
          <span [ngStyle]="{
            'color': user.status === 'active' ? 'green' : 'red',
            'font-weight': 'bold'
          }">
            {{ user.status }}
          </span>
        </p>
      </div>

      <button
        (click)="showDetails = !showDetails"
        [class.expanded]="showDetails">
        {{ showDetails ? 'Weniger' : 'Mehr' }} Details
      </button>

      <div *ngIf="showDetails" class="details">
        <p>User ID: {{ user.id }}</p>
        <p>Erstellt: 01.01.2025</p>
      </div>
    </div>
  </div>

  <ng-template #noUsers>
    <div class="empty-state">
      <p>Keine Benutzer gefunden.</p>
      <button (click)="filterStatus = 'all'">
        Filter zurücksetzen
      </button>
    </div>
  </ng-template>
</div>

Tipps & Tricks

Performance-Tipps

1. TrackBy immer nutzen bei ngFor:

<!-- ❌ Ohne trackBy - Schlechte Performance -->
<div *ngFor="let item of items">

<!-- ✅ Mit trackBy - Gute Performance -->
<div *ngFor="let item of items; trackBy: trackById">

2. ngIf vs. CSS-hidden:

<!-- ngIf entfernt aus DOM (besser für große Components) -->
<app-heavy-component *ngIf="show"></app-heavy-component>

<!-- CSS versteckt nur (besser fürs Toggle) -->
<div [hidden]="!show">Schnell toggle</div>

3. ngFor Limit:

// Slice Pipe für Pagination
```html
<div *ngFor="let item of items | slice:0:10">

Debugging-Trick

JSON Pipe zum Debuggen:

<div *ngFor="let user of users">
  {{ user | json }}  <!-- Zeigt komplettes Object -->
</div>

Häufige Fehler

Fehler 1: Mehrere Structural Directives

FALSCH:

<div *ngIf="isVisible" *ngFor="let item of items">
  <!-- Funktioniert NICHT! -->
</div>

RICHTIG:

<ng-container *ngIf="isVisible">
  <div *ngFor="let item of items">
    {{ item }}
  </div>
</ng-container>

Regel: Nur EINE Structural Directive pro Element!

Fehler 2: trackBy vergessen

Schlecht für Performance:

<div *ngFor="let user of users">

Besser:

<div *ngFor="let user of users; trackBy: trackByUserId">

Fehler 3: ngClass Syntax-Fehler

FALSCH:

<div [ngClass]="active">  <!-- String statt Object/Array -->

RICHTIG:

<div [ngClass]="{'active': isActive}">
<!-- oder -->
<div [ngClass]="['btn', 'btn-primary']">

Fehler 4: * vergessen bei Structural Directives

FALSCH:

<div ngIf="condition">  <!-- * fehlt! -->

RICHTIG:

<div *ngIf="condition">
🔍

Debugging & Troubleshooting

ngIf funktioniert nicht?

Prüfe:

  1. Ist die Bedingung wirklich true/false?
ngOnInit() {
  console.log('Condition:', this.isVisible);
}
  1. Ist die Variable definiert?
isVisible: boolean = false;  // Initialisieren!

ngFor zeigt nichts?

Prüfe:

  1. Ist das Array leer?
ngOnInit() {
  console.log('Items:', this.items, this.items.length);
}
  1. Console Errors?
ERROR: Cannot read property 'name' of undefined
→ Item hat kein 'name' Property!

ngClass funktioniert nicht?

Prüfe:

  1. Sind die CSS-Klassen definiert?
.active { background: blue; }  /* CSS existiert? */
  1. Syntax korrekt?
[ngClass]="{ 'active': isActive }"  <!-- Quotes um Klasse! -->
🎯

Zusammenfassung

Du hast gelernt:

  • ✅ Directives ändern das DOM (Struktur oder Attribute)
  • ✅ *ngIf für Conditional Rendering (Element hinzufügen/entfernen)
  • ✅ *ngFor für Listen (mit trackBy für Performance)
  • ✅ *ngSwitch für Multiple Conditions
  • ✅ ngClass für dynamische CSS-Klassen
  • ✅ ngStyle für dynamische Inline-Styles
  • ✅ Nur EINE Structural Directive pro Element

Die wichtigsten Directives:

DirectiveZweckBeispiel
*ngIfShow/Hide Element<div *ngIf="show">
*ngForListe rendern<li *ngFor="let item of items">
*ngSwitchMultiple Cases<div [ngSwitch]="status">
ngClassCSS-Klassen[ngClass]="{'active': isActive}"
ngStyleInline Styles[ngStyle]="{'color': color}"

Key Takeaways:

  • * = Structural Directive (ändert DOM-Struktur)
  • Nutze trackBy bei ngFor für Performance
  • ngClass für mehrere Klassen, [class.active] für einzelne
  • ng-container für Wrapper ohne Extra-Element
  • ng-template für wiederverwendbare Templates

Quiz

📝 Quiz

Welche Directive entfernt ein Element aus dem DOM?

📝 Quiz

Warum sollte man trackBy bei *ngFor nutzen?

Übungsaufgaben

Aufgabe 1: Todo-Liste mit Filters

Erstelle eine Todo-Liste mit:

  • *ngFor für die Todos
  • *ngIf für "Keine Todos" Nachricht
  • Filter Buttons (Alle, Erledigt, Offen)
  • ngClass für erledigt/offen Styling

Aufgabe 2: User-Karten

Erstelle User-Cards mit:

  • *ngFor über User-Array
  • *ngSwitch für User-Rolle (admin/editor/viewer)
  • ngClass für Rolle-spezifisches Styling
  • trackBy mit User-ID

Aufgabe 3: Progress Dashboard

Erstelle ein Dashboard mit:

  • Mehrere Progress-Bars (*ngFor)
  • ngStyle für dynamische Breite und Farbe
  • *ngIf für Warnung bei < 30%

Nächste Schritte

Im nächsten Artikel:

  • Services erstellen
  • Dependency Injection verstehen
  • Daten zwischen Components teilen
  • Singleton vs. Instance Services

➡️ Weiter zu: Angular Services & Dependency Injection

Du meisterst jetzt Directives! 🪄

AngularLektion 5 von 10
50% abgeschlossen
Lektion abgeschlossen!

Gut gemacht! 🎉

Du hast "Angular Directives - DOM dynamisch steuern" abgeschlossen

Artikel bewerten

0.0 (0 Bewertungen)

Bitte einloggen um zu bewerten