ngFor, ngIf, ng-Template und ein Array


In dieser Lektion soll das bisher Gelernte an einem Beispiel vertieft und praktisch angewendet werden. Eine kleine Anwendung ermöglicht es, über Schaltflächen Elemente zu einem Array hinzuzufügen und wieder zu entfernen. Da sich während der Laufzeit die Anzahl der Elemente ändern kann, eignet sich die Direktive ngFor sehr gut, um den kompletten Inhalt des Arrays darzustellen. Im HTML geschieht das ganz einfach mit einer unsortierten Liste (ul). Für die einzelnen Datensätze wird zunächst das Interface ListEntry definiert.

export interface ListEntry {
  content: string;
  date: Date;
}

Im TypeScript der Komponente stehen zwei Methoden im Mittelpunkt. Die Methode addEntry erzeugt ein neues ListEntry und fügt es dem Array entries hinzu. Die Methode removeEntry kommt zum Einsatz, um Einträge aus dem Array wieder zu entfernen. Ihr wird ein ListEntry übergeben, für das zunächst die entsprechende Indexposition im Array ermittelt werden muss.

Zusätzlich gibt es die Methode generateRandomText, die einen zufälligen Text für ein ListEntry erzeugt. Die Eigenschaft date innerhalb von ListEntry wird stets mit dem aktuellen Datum und der aktuellen Uhrzeit gesetzt.

mport { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ListEntry } from './ListEntry';

@Component({
  selector: 'app-array-sample',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './array-sample.component.html',
  styleUrl: './array-sample.component.scss'
})
export class ArraySampleComponent {

  entries: ListEntry[] = [];

  addEntry(): void {
    const randomText = this.generateRandomText(20);
    const newEntry: ListEntry = {
      content: randomText,
      date: new Date()
    };
    this.entries.push(newEntry);
  }

  removeEntry(entry: ListEntry): void {
    let index = this.entries.indexOf(entry);
    this.entries.splice(index, 1);
  }

  private generateRandomText(length: number): string {
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';
    for (let i = 0; i < length; i++) {
      result += characters.charAt(Math.floor(Math.random() * characters.length));
    }
    return result;
  }
}

Im HTML kommt, wie zuvor angedeutet, ngFor zum Einsatz. Wichtig ist dabei, dass jeder Listeneintrag über einen eigenen Button verfügt, um genau diesen Eintrag entfernen zu können. Beim click-Ereignis wird daher stets das aktuelle ListEntryübergeben. Im TypeScript nimmt die Methode removeEntry dieses Objekt entgegen und entfernt es aus dem Array. Durch die Änderungserkennung von Angular wird die Liste automatisch aktualisiert, sobald ein Eintrag hinzugefügt oder gelöscht wird.

<button (click)="addEntry()">Add</button>

<ul>
  <li *ngFor="let entry of entries">
    <div>Content: {{ entry.content }}</div>
    <div>Date: {{ entry.date | date:'dd.MM.yyyy HH:mm:ss' }}</div>
    <button (click)="removeEntry(entry)">Remove</button>
  </li>
</ul>

Beim Start der Anwendung oder nachdem alle Einträge gelöscht wurden, wird die Liste nicht angezeigt. In vielen Situationen ist es jedoch sinnvoll, einen alternativen Text einzublenden. Ein typisches Beispiel wäre eine Suche: Durch einen Hinweistext erfährt der Nutzer, dass die Suche kein Ergebnis geliefert hat. Würde stattdessen einfach nichts angezeigt, könnte leicht der Eindruck entstehen, die Suche sei noch nicht abgeschlossen.

In der Programmlogik ergeben sich damit zwei mögliche Zustände:

Entweder enthält das Array Einträge, die dargestellt werden können, oder es ist leer. Im ersten Fall wird wie gewohnt die Liste angezeigt, im zweiten Fall ein Platzhaltertext. Zunächst soll dies mit zwei einfachen ngIf-Abfragen umgesetzt werden, die die Anzahl der Einträge im Array prüfen.

<button (click)="addEntry()">Add</button>

<div *ngIf="entries.length === 0">
  No entries
</div>

<div *ngIf="entries.length > 0">
  <ul>
    <li *ngFor="let entry of entries">
      <div>Content: {{ entry.content }}</div>
      <div>Date: {{ entry.date | date:'dd.MM.yyyy HH:mm:ss' }}</div>
      <button (click)="removeEntry(entry)">Remove</button>
    </li>
  </ul>
</div>

Obwohl diese Variante korrekt funktioniert, ist sie nicht besonders elegant. Vor allem stört, dass dieselbe Bedingung zweimal formuliert werden muss. Praktischer wäre ein If-Else-Block, wie er in vielen Programmiersprachen üblich ist. Ein solcher Block steht an dieser Stelle und mit dieser Angular-Version aber nicht direkt zur Verfügung.

Abhilfe schafft hier die Direktive ng-Template. Mit ihr lässt sich ein HTML-Block definieren, der angezeigt wird, wenn eine Bedingung nicht erfüllt ist. Angesprochen wird ein solches Template über einen frei wählbaren Namen. In diesem Beispiel lautet dieser Name noData. Das Template kann im HTML an beliebiger stelle stehen. Vor oder nach dem ngIf.

<button (click)="addEntry()">Add</button>

<div *ngIf="entries.length > 0; else nodata">
  <ul>
    <li *ngFor="let entry of entries">
      <div>Content: {{ entry.content }}</div>
      <div>Date: {{ entry.date | date:'dd.MM.yyyy HH:mm:ss' }}</div>
      <button (click)="removeEntry(entry)">Remove</button>
    </li>
  </ul>
</div>

<ng-template #nodata>
  <div>
    No entries
  </div>
</ng-template>

Diese Schreibweise kommt einem If-Else-Block bereits deutlich näher. Der Code lässt sich jedoch noch weiter verfeinern, indem sowohl der If-Block als auch der Else-Block in eigenen ng-template-Elementen definiert werden. Die Syntax erinnert dann stark an die aus anderen Programmiersprachen bekannte if-then-else-Struktur.

Ein wesentlicher Unterschied besteht jedoch darin, dass die Blöcke im Template nicht zwingend direkt unterhalb der Bedingung stehen müssen. Sie können an einer beliebigen Stelle im HTML definiert werden, solange sie über ihre Referenznamen angesprochen werden.

<button (click)="addEntry()">Add</button>

<div *ngIf="entries.length > 0 then list else nodata"></div>

<ng-template #list>
  <div>
    <ul>
      <li *ngFor="let entry of entries">
        <div>Content: {{ entry.content }}</div>
        <div>Date: {{ entry.date | date:'dd.MM.yyyy HH:mm:ss' }}</div>
        <button (click)="removeEntry(entry)">Remove</button>
      </li>
    </ul>
  </div>
</ng-template>

<ng-template #nodata>
  <div>
    No entries
  </div>
</ng-template>

Eine weitere Verbesserung bietet die mit Angular 17 eingeführte, verkürzte Syntax mit @if und @for. Neben diesen beiden Kontrollstrukturen gibt es jetzt zusätzlich ein @else. Dadurch wird der Code noch kompakter und übersichtlicher. Ganz im Sinne einer modernen Programmiersprache.

<button (click)="addEntry()">Add</button>

@if (entries.length > 0) {
  <div>
    <ul>
      @for (entry of entries; track entry) {
        <li>
          <div>Content: {{ entry.content }}</div>
          <div>Date: {{ entry.date | date:'dd.MM.yyyy HH:mm:ss' }}</div>
          <button (click)="removeEntry(entry)">Remove</button>
        </li>
      }
    </ul>
  </div>
} @else {
  <div>
    No entries
  </div>
}
© 2025 Holger Hinzberg Contact