Tipps und Tricks mit AngularJS, Teil 7: GUIs mit Angular 2 und Redux-Implementierung @ngrx/store (I)

Angular 2

Inhaltsverzeichnis

Mit @ngrx/store existiert eine Umsetzung der Idee hinter Redux für Angular 2. Da ein Mitglied des Angular-2-Teams die Bibliothek federführend entwickelt, hat sie die Bedeutung eines De-facto-Standards. Sie bietet der Anwendung für einzelne Teile des State Trees sogenannte Observables an. Dabei handelt es sich um Objekte, die bei Zustandsänderungen Nachrichten veröffentlichen. Angular 2 ist mit ihnen in der Lage, die Performance von Ansichten zu beschleunigen, da es Observables nicht auf Änderungen überwachen muss. Stattdessen hat das JavaScript-Framework lediglich zu warten, bis sie es über eine Änderung informieren und daraufhin die Ansicht aktualisieren. Zudem bietet Angular 2 auch Performanceoptimierung für die Arbeit mit unveränderbaren Datenstrukturen wie dem vom Store verwalteten Single Immutable State Tree.

Für Funktionen, die als Reducer fungieren sollen, stellt @ngrx/store den Typ Reducer<T> zur Verfügung. Der Typparameter T ist dabei durch den Typ zu ersetzen, der den betroffenen Teilbaum des Immutable State Trees repräsentiert:

export const boardingReducer: Reducer<BoardingState> = (state:
BoardingState, action:Action) => {
switch (action.type) {
case AKTION1: return action1(state, action.payload);
case AKTION2: return action2(state, action.payload);
default: return state;
}
}

Diese Funktionen nehmen den aktuellen Zustand sowie das Aktionsobjekt entgegen und liefern einen neuen Zustand zurück. Das Beispiel führt dazu in Abhängigkeit vom empfangenen Aktionsobjekt eine Operation aus. Aktion hat zwei Eigenschaften: Der Typ (type) identifiziert die Aktion. Dabei handelt es sich lediglich um einen String. Die Payload (payload) hingegen umfasst die von der Aktion benötigten Parameter. Um Missverständnisse zu vermeiden, bietet es sich an, die einzelnen Aktionstypen über Konstanten zu definieren:

export const AKTION1 = 'AKTION1';
export const AKTION2 = 'AKTION2';

Damit der Store den einzelnen Komponenten und Services zur Verfügung steht, ist er beim Bootstrapping der Angular-2-Anwendung über einen Provider bereitzustellen. Hierzu bietet @ngrx/store die Funktion provideStore:

var initAppState: AppState = {
boarding: initialBoardingState
}

services = [
[...]
provideStore({boarding: boardingReducer}, initAppState)
];

bootstrap(AppComponent, services);

Beim ersten Parameter von provideStore handelt es sich um ein Objekt mit einem Reducer für jeden Zweig der ersten Hierarchieebene des State Tree. Die Eigenschaften des Objekts müssen die Namen der Zweige aufweisen. Der zweite Parameter verweist auf die Ausgangsversion des gesamten State Tree. Einzelne Komponenten und Services können sich den so registrierten Store in der Folge injizieren lassen:

export class BoardingComponent {
constructor(private store: Store<AppState>) {
[...]
}
[...]
}

Über den Store erlangen sie Zugriff auf den State Tree. Er bietet beispielsweise mit der Funktion select Observables für die benötigten Teile des Zustands an:

this.buchungen = this.store.select(s => s.boarding.buchungen);

Komponenten und Services können sich über die Methoden der Observables über Änderungen informieren lassen. Darüber hinaus sind Komponenten in der Lage, Observables direkt im Rahmen der Datenbindung zu nutzen. Dazu kommt die Pipe async zum Einsatz:

<tr *ngFor="#b of buchungen | async">
<td>{{b.name}}</td>
[...]
</tr>

Sie aktualisiert zudem die Datenbindung, wenn sich der dahinter stehende Wert ändert. Zum Modifizieren des Zustands nutzt die Anwendung hingegen die Methode dispatch. Sie nimmt ein Aktionsobjekt entgegen und führt die dazugehörigen Aktionen innerhalb der Dispatcher im Store aus:

this.store.dispatch({ type: AKTION1, payload: someData });