AUTOR Matthias vom Bruch

 

Radikale Reaktivität in Angular Teil 1

Eine Großer-Schritt-für-Großer-Schritt-Anleitung vom Angular-Anfänger zum RxJS-Experten
 

Einführung in RxJS

Die Bibliothek RxJS (das "subscribe"-Ding), die stark in Angular integriert ist, ermöglicht es uns, hoch reaktiven, funktionalen Code zu schreiben. Aber um ihre Vorteile wirklich zu nutzen, müssen wir uns die Denkweise eines funktionalen Programmierers aneignen, um Fehler- und Memory-leakfreien Code zu produzieren.
 
Wenn man die Dokumentation von RxJS liest, lernt man die zentralen Artefakte der Bibliothek kennen, wie z.B. das Observable selbst, verschiedene Arten von Subjects, Operatoren, Schedulern usw. Aber es ist schwierig, daraus die richtige Art und Weise abzuleiten, wie diese anzuwenden sind - was sind Anti-Patterns, Fallstricke, die man vermeiden sollte, und wie fange ich überhaupt an?

Weil RxJS so fest in Angular integriert ist, sind viele Projekte zwar gezwungen, RxJS zu verwenden, tun es aber auf eine verzerrte, unsichere Weise, die daran erinnert, wie wir Promises verwendet haben.

Wie also habe ich gelernt, RxJS zu nutzen? Durch viele Ratschläge meines Teamleiters und 6 Monate Arbeit an einem PoC für einen Stil, den ich "radikale Reaktivität" nenne. Wir wollten ausprobieren, wie weit wir mit dem MEAN-Stack an der reaktiven Front gehen können.
 
Das Ergebnis ist eine Anwendung, bei der jeder Aspekt des Zustands direkt mit den Daten im Backend verknüpft ist. Selbst während man z. B. den Namen einer Person bearbeitet, ändert sich der Wert im Formularfeld, wenn ein anderer Benutzer zur gleichen Zeit das Selbe tut. Und seitdem habe ich den Code in weniger aufwendigen Projekten verbessert.

 

In diesem Artikel werde ich versuchen zu zeigen, wie typischer Anfängercode aussieht, welche Probleme er verursacht und wie man sie beheben kann. Die Demo-App zeigt sogar, welches Zustandsdesign für reaktiv eintreffende Updates optimal ist, um den geringsten "Wartungsaufwand" zu haben.

 

Sie finden alle Beispiele der Demo-App auf meinem GitHub. Klonen Sie den Code, führen Sie `npm install` und `npm start` aus und beginnen Sie zu experimentieren.

 

Demo-Anwendung

Ich habe eine Demo-Applikation geschrieben, die es einem imaginären Agenten erlaubt, Kunden und Autos zu verwalten und den Kunden Autos zuzuweisen. Stellen Sie sich das als die Benutzeroberfläche eines Angestellten in einer etwas seltsamen Autovermietung vor.

 

Die Benutzeroberfläche der Demo-AppDemo App

Diese Demo-Anwendung besteht aus mehreren verschiedenen Phasen, die in Git-Branches organisiert sind und alle die gleiche Funktionalität bieten, aber mit unterschiedlicher Codequalität implementiert sind.

 

Ich empfehle Ihnen dringend, sich das Repository anzuschauen und den Code dort zu studieren. Hunderte von Codezeilen hier einzufügen, wird nicht hilfreich sein, daher werde ich nur die wichtigsten Punkte erläutern.

 

So funktionieren die Branches des Repositorys:

- naive-implementation versucht, typischen Anfängercode zu reproduzieren. Versuchen Sie, die Probleme zu finden, die dieser Code hat. Fragen, die Sie sich stellen können, sind "wie funktionieren Observables, und was ist das Besondere an Observables, die vom Angular http-Client zurückgegeben werden?", "warum brauchen wir Services in Angular?" und "warum benutzen wir Typescript?".

 

- highlight-problems verwendet den selben Code, hat aber lange Kommentare, die erklären, warum bestimmte Teile des Codes problematisch sind. Es lenkt Ihre Aufmerksamkeit auf typische Anti-Patterns, die es sehr einfach machen, den Code eines Anfängers von dem eines Experten zu unterscheiden.

 

- refactor-component behandelt diese Probleme unter der Annahme, dass Sie nur die Komponente ändern können. Dies ist eine typische Situation in großen Projekten, in denen man nicht einfach Dinge im Servicelayer ändern kann. Zugegeben, dieser Code ist sehr kompliziert, aber er zeigt gut, wie RxJS-Code durch Komposition an Struktur gewinnt, und behandelt bereits einige Edge-Cases, bei denen wir fortgeschrittene Tricks brauchen, damit unser Event-Netzwerk reibungslos funktioniert.

 

- move-to-service beginnt, wie professioneller Code auszusehen, indem die gesamte Logik, die sich mit der Zustandsverwaltung befasst, in den Servicelayer verlagert wird, wo sie wiederverwendet werden kann. Unsere Komponentenklasse schrumpft beträchtlich, da sie jetzt nur noch die Komplexität verwalten muss, die der Komponente selbst innewohnt.

 

- reactive-flux-cache geht davon aus, dass unser Dienst über einen Kanal für Echtzeit-Updates aus dem Backend verfügt, so dass der Zustand immer auf dem neuesten Stand gehalten wird. Dies kann zum Beispiel mit Hilfe eines Websockets implementiert werden. In dieser Anwendung wird das Backend natürlich gemockt, so dass es nicht notwendig ist, dies tatsächlich zu implementieren. Wenn wir jedoch davon ausgehen, dass wir eine Flux-Architektur bis hin zum Backend haben, können wir die Art und Weise, wie der Zustand verwaltet wird und die Kommunikation stattfindet, grundlegend ändern. Auch die Änderung der "Backend-API", die ihre Daten in einem einfacheren Format liefert, ermöglicht es uns, die Menge an Logik zu reduzieren, die erforderlich ist, um Datenänderungen in den Zustand zu integrieren.

 

- Die letzte wichtige Hürde wird mit component-borders überwunden: Wie kann man Daten zwischen Eltern- und Kindkomponente reaktiv weitergeben? Ich schlage ein allgemeines Muster vor, das auch für andere Fälle funktioniert, wie @ViewChild(). Die Verwendung dieser Technik bietet ein wiederverwendbares Pattern für die Datenübergabe in der reaktiven Welt und ermöglicht es Ihnen, die Anzahl der Lifecycle-Hooks, die Ihre Komponenten benötigen, drastisch zu reduzieren.

 

In den nächsten Beiträgen werden wir einige der Probleme besprechen, die wir im Demoprojekt gefunden haben, und einige, die darüber hinausgehen. Natürlich mit Vorschlägen, wie man sie verbessern kann. Sie konzentrieren sich auf

 

- Composition

- Fehlerbehandlung

- Vermeidung von Memory-Leaks

- Vermeidung von Seiteneffekten

- Austausch von Daten zwischen Komponenten

 

 

 

 

 

Über den Autor

Matthias erreicht gerne mit wenig Aufwand große Ziele und löst komplexe Probleme nur einmal, aber korrekt. Als Senior Web Entwickler lebt er diese Philosphie im Umfeld von Angular, VueJS, Deployment Pipelines, UI-Libraries, State-Management Systemen, Typescript-Backends und was sonst eben gerade seine Aufmerksamkeit benötigt. Gerne lässt er sich dabei von Kollegen inspirieren und übernimmt Konzepte von verschiedenen Technologien, wie RxJS, NixOS, oder Rust. Auch gibt er dieses Wissen stets gerne weiter, um die Entwicklung und das Fortbestehen performanter, glücklicher Entwicklerteams zu fördern.