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

Nicht nur React-Entwickler können mit Redux den gesamten Anwendungszustand in einer zentralen Datenstruktur verwalten. @ngrx/store gibt Angular-2-Nutzern dieselben Optionen, um die Komplexität ihrer GUI zu verringern.

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

In AngularJS-Anwendungen ist es gute Praxis, den Zustand der einzelnen Ansichten in Services zu verwalten. So können unterschiedliche Bereiche der Applikation darauf zugreifen. Außerdem bleiben die in Services hinterlegten Informationen erhalten, wenn der Benutzer die aktuelle Ansicht verlässt.

Obwohl dieser Ansatz zunächst vernünftig klingt, steigert er die Komplexität und schränkt somit die Nachvollziehbarkeit der Anwendung ein. Abbildung 1 veranschaulicht das anhand eines Beispiels, in dem jeder Teil der Anwendung direkten Zugriff auf den auf Services verteilten Gesamtzustand hat. Dadurch ist es schwer verständlich, warum bestimmte Zustandsänderungen eingetreten sind. Durch die Verteilung des Zustands ergibt sich darüber hinaus die Gefahr, dass er redundant in unterschiedlichen Bereichen vorliegt. Berücksichtigt die Anwendung nicht ständig sämtliche Redundanzen, sind Inkonsistenzen unvermeidlich.

In regulären Anwendungen ist der Zuständ häufig über mehrere Services verteilt (Abb. 1)

Der aus React bekannte Redux-Ansatz verspricht Abhilfe, indem er ein zentrales und kontrolliertes Verwalten des Anwendungszustandes vorsieht. Der vorliegende Beitrag stellt die Methode vor dem Hintergrund von Angular 2 vor. Dazu geht er auf die Redux-Implementierung @ngrx/store ein. Ein weiterer Artikel ist ihrem praktischen Einsatz gewidmet.

Um Übersichtlichkeit zu gewährleisten, sieht Redux die Verwaltung des Anwendungszustands in einem einzigen baumförmigen Objektgraphen vor. Letzterer ist unter dem Namen Single Immutable State Tree bekannt, es handelt sich folglich um eine nicht veränderbare Datenstruktur. Das bedeutet, dass die Anwendung sie im Zuge von Änderungen gegen eine neue ersetzen muss.

Der von funktionalen Sprachen bekannte Ansatz birgt einige Vorteile. Beispielsweise kann ein Framework wie Angular 2 anhand derartiger Strukturen einfacher überwachen, ob es Änderungen gab. Dazu ist lediglich zu prüfen, ob sich die Datenstruktur als Ganzes gewandelt hat. Eine Änderungsverfolgung auf der Ebene der einzelnen Eigenschaften und untergeordneten Objekte ist nicht notwendig, da sie ohnehin nicht mutabel sind. Aber auch ein Zurückkehren zu vorherigen Zuständen (undo) oder ein Protokollieren einzelner Zwischenzustände ist beim Einsatz unveränderbarer Strukturen für die Anwendung einfacher zu realisieren.

Wie Abbildung 2 veranschaulicht, verwaltet beim Einsatz von Redux ein sogenannter Store den Single Immutable State Tree. Dabei handelt es sich aus Sicht von Angular 2 um einen Service, der den Zugriff auf den Anwendungszustand kontrolliert und Anwendungsteilen Lesezugriff gewährt. Um den Zustand zu verändern, senden die einzelnen Komponenten Aktionen (Actions) an den Store. Das sind Objekte, die die gewünschte Änderung beschreiben. Der Store verteilt die Aktionen an sogenannte Reducer, die für einen Teilbaum des State Tree verantwortlich sind und ihn in Hinblick auf die empfangenen Aktionen gegen eine aktualisierte Version tauschen.

Komponenten senden Aktionen, um in einer Redux-Anwendung den Gesamtanwendungszustand zu ändern (Abb. 2).

Die Anwendung kann für jeden Teilbaum auf der höchsten Hierarchieebene einen Reducer registrieren. Es bietet sich somit an, Teilbäume jeweils einen bestimmten Bereich der Applikation widerspiegeln zu lassen. Der Store sendet alle empfangenen Aktionen an sämtliche Reducer weiter. Sie entscheiden, ob beziehungsweise wie sie diese abarbeiten.

Der Reducer nimmt seinen Teilbaum sowie die empfangene Aktion entgegen und liefert anschließend einen neuen Teilbaum, der den aktuellen Zustand repräsentiert. Es handelt sich dabei um eine sogenannte pure Funktion, deren Rückgabewerte einzig von den übergebenen Parametern abhängen. Das Berücksichtigen anderer Objekte ist nicht erlaubt. Auf die Weise gestalten sich Funktionen nachvollziehbar und sie sind einfacher automatisiert testbar.