Angular Ivy führt zwei zentrale Konzepte ein, die die Verwaltung und Verteilung von Abhängigkeiten tiefgreifend verändern: den any- und den platform-Provider-Scope. Diese Mechanismen erlauben eine feingranulare Kontrolle darüber, wie Services instanziiert und geteilt werden – entweder innerhalb eines Moduls oder über mehrere Angular-Applikationen hinweg. Das Verständnis dieser Scopes ist essenziell, insbesondere in komplexeren Anwendungen mit modularer Architektur oder in Multi-App-Setups wie Microfrontends oder Angular Elements.
Beginnen wir mit dem any-Scope. Dieser Scope ist eng mit dem jeweiligen Modul-Injektor verbunden. Wenn ein Service mit providedIn: 'any' versehen ist, erzeugt Angular pro Modul-Injektor eine eigene Instanz dieses Services. Das bedeutet: Wird derselbe Service in mehreren Modulen injiziert, und jedes dieser Module besitzt seinen eigenen Injektor, entstehen auch mehrere voneinander isolierte Instanzen. Diese Struktur erlaubt es, Abhängigkeiten wie Konfigurationen oder Zustandsobjekte lokal pro Modul zu definieren, ohne dass es zu ungewollten Seiteneffekten durch geteilte Instanzen kommt.
Ein konkretes Beispiel illustriert dies: Das Modul SharesModule stellt eine spezifische Backend-Konfiguration bereit. Der zugehörige SharesComponent verwendet den BackendService, welcher diese lokale Konfiguration nutzt. Da der Service durch den any-Scope bereitgestellt wird, erhält SharesComponent eine Instanz von BackendService, die an die lokale Konfiguration von SharesModule gebunden ist. Im Gegensatz dazu steht das BankAccountsModule, das keine eigene Konfiguration bereitstellt. BankAccountsComponent nutzt ebenfalls BackendService, greift aber – mangels lokaler Bereitstellung – auf die im AppModule global definierte Backend-Konfiguration zurück.
Trotz nahezu identischem Aufbau der beiden Komponenten (SharesComponent und BankAccountsComponent) unterscheiden sich die zugrunde liegenden Instanzen des BackendService, was sich direkt auf die Funktionalität auswirkt. Diese Modularität im Scope-Management erhöht die Testbarkeit, die Isolation von Features und verbessert die Wartbarkeit von Anwendungen mit umfangreicher Feature-Architektur.
Der platform-Scope hingegen agiert auf einer ganz anderen Ebene. Er zielt nicht auf einzelne Module innerhalb einer Applikation, sondern auf die gesamte Plattform – also alle Angular-Anwendungen, die auf einer gemeinsamen Seite gebootstrapped werden. Dienste oder Werte, die über providedIn: 'platform' bereitgestellt werden, sind somit einmalig pro Seite vorhanden und werden von allen Applikationen geteilt. Dies ist besonders bei globalen APIs oder nativen Browser-Objekten wie localStorage nützlich, bei denen eine zentrale, konsistente Instanz über Applikationsgrenzen hinweg erforderlich ist.
Ein praktisches Beispiel ist die Bereitstellung eines storageToken, der das localStorage-Objekt kapselt. Durch die Deklaration providedIn: 'platform' wird sichergestellt, dass unabhängig davon, ob der Token in einer Dokumenten- oder Musikapplikation genutzt wird, stets dieselbe Instanz – also das native localStorage – verwendet wird. Diese Struktur ist nicht nur logisch, sondern reduziert auch Redundanz und stellt konsistente Datenhaltung sicher.
Bemerkenswert ist, dass für den Entwickler kein zusätzlicher Aufwand beim Konsumieren solcher platform-gescopten Abhängigkeiten entsteht. Der Code bleibt identisch zur klassischen Abhängigkeitsinjektion. Der Unterschied liegt ausschließlich in der Bereitstellung, also der Sphäre, in der Angular die Instanz erzeugt und verfügbar macht.
Zusammenfassend ergibt sich ein klares Bild der Vorteile beider Scopes: Der any-Scope eignet sich für modulare, in sich gekapselte Features mit jeweils eigener Konfiguration oder Zustandsverwaltung. Er ermöglicht das, was im klassischen Angular oft nur mit forRoot()- bzw. forChild()-Mustern erreicht werden konnte – nur wesentlich flexibler und ohne das Risiko, versehentlich globale Singletons zu erzeugen. Der platform-Scope dagegen eignet sich für den systemweiten, übergreifenden Zugriff auf zentrale Ressourcen, wie native Web-APIs oder globale Konfigurationsobjekte in Microfrontend-Architekturen.
Wichtig für den Leser ist das tiefere Verständnis der Hierarchie von Injektoren in Angular. Jeder Modul-Injektor bildet eine abgeschlossene Einheit, innerhalb derer any-gescopte Abhängigkeiten eindeutig sind. Nur wenn keine Konfiguration lokal verfügbar ist, wird auf übergeordnete Injektoren – bis hinauf zum Root Injector – zurückgegriffen. Das bedeutet, dass eine scheinbar identische Komponente je nach Modulkontext unterschiedliche Services injiziert bekommt. Dies ist nicht nur ein leistungsfähiges Feature, sondern kann auch Ursache schwer zu entdeckender Fehler sein, wenn das Scoping nicht bewusst und konsistent gesteuert wird.
Ebenso sollte beachtet werden, dass der platform-Scope zwar global agiert, jedoch nur innerhalb des Browserkontextes einer einzelnen Seite gültig ist. Wird dieselbe Applikation mehrfach in unterschiedlichen Browser-Tabs geladen, existiert der platform-gescopte Service pro Tab erneut. Er ist also plattformweit auf Seitebene, nicht prozess- oder systemweit.
Zudem ist es sinnvoll, bei der Verwendung von platform-gescopten Tokens oder Services stets auf die Kompatibilität und Stabilität der gemeinsam genutzten Abhängigkeiten zu achten. Eine Änderung in einer Applikation kann sich direkt auf eine andere auswirken – was in hochmodularen Umgebungen zu Integrationsproblemen führen kann.
Wie man Datenmodell und Komponenten in einer Angular-Anwendung strukturiert und verwendet
Um die verschiedenen Komponenten einer Angular-Anwendung effizient zu nutzen, ist es sinnvoll, die Datenabfrage und -speicherung von der eigentlichen Verwendung der Komponenten zu trennen. Dies ermöglicht eine klarere Struktur und eine bessere Wartbarkeit der Anwendung. Bevor wir also mit der Implementierung der neuen Angular-Komponenten beginnen, müssen wir ein solides Datenmodell aufbauen.
Das Datenmodell dient dazu, die Struktur der Daten festzulegen, die wir in unserer Anwendung verwenden werden. In diesem Fall beschreiben wir das Modell mithilfe von TypeScript-Interfaces und kommunizieren mit dem Backend über Angular-Dienste. Ein einfaches, aber effektives Modell reicht aus, um die benötigten Informationen zu organisieren. Die wichtigsten Modelle, die in diesem Zusammenhang verwendet werden, umfassen die Entitäten „School“, „Course“ und „Video“.
Ein Kurs hat einen Titel, eine optionale Beschreibung und eine Liste von Videos, die der Benutzer ansehen kann. Das Video-Modell enthält grundlegende Informationen wie die ID des Videos auf YouTube, den Titel, das Upload-Datum und den Autor. Schließlich enthält das Schulmodell einen Namen sowie die geografischen Koordinaten (Breiten- und Längengrad), damit die Schule auf einer Karte angezeigt werden kann. Die Schulen bieten eine Reihe von Kursen an, die jeweils mit den entsprechenden Videos verknüpft sind.
Die Trennung der verschiedenen Module in Angular hilft uns, die Anwendung übersichtlich und modular zu gestalten. So haben wir die Hauptkomponenten in drei Hauptmodule unterteilt: Kurs, Thema und Schulen. Jedes dieser Module hat eine spezifische Funktion: Das Kurs-Modul zeigt die Kurse an, das Thema-Modul steuert das Aussehen der Anwendung und das Schulen-Modul zeigt eine Karte an, auf der die Schulen zu finden sind. Die Strukturierung dieser Module ermöglicht es, die Logik und die Abhängigkeiten klar zu trennen, was die Wartbarkeit erheblich verbessert.
Ein weiterer wichtiger Schritt ist die Definition von Routen, die es dem Benutzer ermöglichen, zwischen den verschiedenen Komponenten der Anwendung zu navigieren. Zum Beispiel wird der Benutzer zunächst zu einem Kurs weitergeleitet, basierend auf der ID des Kurses. Sobald er auf die Schule zugreift, kann er den Kurs wählen, den er belegen möchte. Die Navigation erfolgt durch das RouterModule von Angular, das die verschiedenen Routen und deren Komponenten verwaltet.
Die Nutzung von Angular-Modulen für die Verwaltung von Abhängigkeiten spielt eine wesentliche Rolle bei der Strukturierung der Anwendung. Jedes Modul enthält nur die Abhängigkeiten, die es benötigt, und sorgt dafür, dass die Anwendung insgesamt übersichtlich bleibt. So enthält das Kurs-Modul beispielsweise das Video-Modul und das Schul-Modul enthält das GoogleMaps-Modul. Dies vereinfacht nicht nur die Verwaltung der Abhängigkeiten, sondern auch die Erweiterung der Anwendung in der Zukunft.
Die Datenabfrage erfolgt durch die Verwendung von Angular-Diensten. Der Kursdienst (CourseService) und der Schulservice (SchoolsService) sind dafür verantwortlich, die benötigten Daten asynchron abzurufen. Der Kursdienst holt einen einzelnen Kurs ab, während der Schulservice eine Liste von Schulen bereitstellt, die dann wiederum Kurse anbieten. Es ist wichtig zu verstehen, dass die Dienste mit mock-Daten arbeiten, aber in einer realen Anwendung diese Daten von einem Server abgerufen würden.
Durch den gezielten Einsatz von Modulen und Diensten können wir die Navigation und die Datenabfrage effektiv steuern und dabei eine klare Struktur für die Anwendung bewahren. Die Nutzung von Angular-Komponenten und die Trennung von Datenmodell, Komponenten und Modulen ist der Schlüssel zu einer wartbaren und skalierbaren Anwendung.
Es ist wichtig, dass beim Entwickeln von Angular-Anwendungen stets darauf geachtet wird, dass die Architektur so modular wie möglich bleibt. Dies fördert nicht nur eine klare Trennung der Verantwortlichkeiten, sondern hilft auch dabei, die Anwendung leichter zu testen und zu erweitern. Auch das Verständnis von Angular-Routen und deren Implementierung ist für den Erfolg einer Anwendung entscheidend, da dies die Navigation innerhalb der Anwendung steuert und den Benutzerfluss maßgeblich beeinflusst.
Wie man mit Component Harnesses in Angular effizient Tests durchführt
Im Bereich des Testens von UI-Komponenten in Angular-Anwendungen stoßen Entwickler häufig auf das Problem, dass Tests eng mit der Struktur des DOM (Document Object Model) gekoppelt sind. Das bedeutet, dass Änderungen im DOM direkt Auswirkungen auf die Tests haben können, was zu instabilen Testfällen führt. Angular Ivy bringt eine neue Lösung für dieses Problem: die Verwendung von "Component Harnesses". Diese ermöglichen es, eine API für Tests zu entwickeln, die das klassische Page-Object-Muster auf eine granularere Ebene anwenden und damit das Testen von Komponenten erheblich vereinfachen.
Ein wichtiger Vorteil von Angular Ivy ist, dass die Component Harnesses für viele Angular Material-Komponenten bereits integriert sind. In diesem Kapitel wird erläutert, wie man bestehende Harnesses verwendet und wie man eigene Harnesses erstellt, um die Tests für Angular-Komponenten zu optimieren.
Verwendung der Component Harnesses von Angular Material
Ein einfaches Beispiel für den Einsatz von Angular Material Harnesses ist das Testen von Material-Komponenten wie MatInputHarness oder MatSliderHarness. Angenommen, wir haben eine Theme-Komponente, mit der der Benutzer die Hintergrundfarbe für die Header-Leiste einstellen kann. Um diese Funktion zu testen, können wir einen MatInputHarness verwenden, um auf das Eingabefeld zuzugreifen und die Farbe zu validieren. Ein Beispiel-Test könnte folgendermaßen aussehen:
Dieser Test prüft, ob der Standardwert für die Header-Hintergrundfarbe korrekt gesetzt ist. Ein ähnlicher Test könnte auch dafür verwendet werden, die Einstellung zu ändern und zu überprüfen, ob die Änderung im Theme-Service reflektiert wird:
In diesem Fall wird die Farbe auf #ffbbcc gesetzt und überprüft, ob diese Änderung im Theme-Service gespeichert wurde. Ein bemerkenswerter Aspekt dieses Tests ist, dass keine fixture.detectChanges()-Methode benötigt wird, da die Component Harnesses die DOM-Operationen intern behandeln und somit eine stabile und weniger fehleranfällige Testumgebung schaffen.
Auch bei der Verwendung von MatSliderHarness für Schieberegler-Komponenten wie der Video-Größenanpassung ist der Testprozess ähnlich. Der Vorteil liegt in der Abstraktion der DOM-Interaktionen, die ansonsten aufwendig und fehleranfällig sein könnten.
Erstellung eines eigenen Component Harness
Nachdem wir nun die Verwendung bestehender Angular Material Harnesses verstanden haben, ist der nächste Schritt die Erstellung eines eigenen Harnesses für benutzerdefinierte Komponenten. Nehmen wir an, wir möchten eine Video-Komponente für die Angular Academy-App erstellen und diese für Tests zugänglich machen. Um dies zu erreichen, können wir einen eigenen ComponentHarness schreiben, der die Interaktionen mit der Video-Komponente kapselt.
Ein Beispiel für einen solchen Harness könnte folgendermaßen aussehen:
Mit diesem VideoHarness können wir nun die Textinhalte von Videos testen und sicherstellen, dass die Anzeige korrekt ist. Der Test selbst könnte so aussehen:
Dieser Test stellt sicher, dass der Titel jedes Videos korrekt gerendert wird, indem die getText()-Methode des VideoHarness verwendet wird. Der Vorteil dieses Ansatzes liegt in der Trennung von Testlogik und DOM-Interaktionen: Der Test muss sich nicht um die Details der Video-Komponente kümmern, sondern kann sich auf das Testen der Funktionalität konzentrieren.
Implementierung und Weiterentwicklung von Component Harnesses
Das Erstellen eines eigenen Harness für benutzerdefinierte Komponenten ermöglicht eine feinere Kontrolle über das Testverhalten und vereinfacht die Wartung von Tests im Laufe der Zeit. Darüber hinaus können wir Details der Implementierung in zukünftigen Versionen der Komponentenbibliothek ändern, ohne dass bestehende Tests betroffen sind. Beispielsweise könnte der VideoHarness auf Änderungen im DOM reagieren, ohne dass die Testfälle neu geschrieben werden müssen, da alle DOM-Operationen durch den Harness abstrahiert werden.
Ein weiteres Beispiel für einen weiterentwickelten Harness ist der YoutubePlayerHarness, der spezifische Details zum YouTubePlayer in einem separaten Harness kapselt:
Dieser YoutubePlayerHarness stellt sicher, dass Tests für die Video-Komponente unabhängig von der Implementierung des YouTube Players durchgeführt werden können, was den Testprozess weiter vereinfacht und die Wartbarkeit erhöht.
Fazit
Die Verwendung von Component Harnesses bietet einen klaren Vorteil bei der Entwicklung von stabilen und wartungsfreundlichen Tests für Angular-Komponenten. Durch die Kapselung von DOM-Operationen und die Anwendung des Page-Object-Musters auf granularer Ebene wird das Testen von Komponenten nicht nur vereinfachter, sondern auch robuster. Indem man eigene Harnesses für benutzerdefinierte Komponenten erstellt, kann man die Tests weiter abstrahieren und flexibel auf Änderungen in der Implementierung reagieren, ohne die Testlogik anpassen zu müssen. Das Ergebnis ist ein effizienter und sauberer Testprozess, der sich positiv auf die Qualität und Wartbarkeit des Codes auswirkt.
Wie man von Angular View Engine zu Angular Ivy migriert: Wichtige Veränderungen und Best Practices
Bei der Migration von Angular View Engine zu Angular Ivy ergeben sich eine Reihe von Änderungen und Herausforderungen, die sowohl die Struktur als auch die Performance von Angular-Anwendungen betreffen. Eine der zentralen Veränderungen, die Entwickler berücksichtigen müssen, betrifft die Art und Weise, wie Abfragen für View- und Content-Elemente behandelt werden. Insbesondere hat Angular Ivy die Behandlung von @ViewChild-Dekoratoren verändert, was in Version 8 zu einer signifikanten Umstellung führte.
In Version 8 mussten Entwickler explizit den static-Schalter definieren, um anzugeben, ob eine View- oder Content-Abfrage statisch oder dynamisch ist. Zum Beispiel wurde der folgende Code verwendet:
Mit dem Wechsel zu Angular Ivy in Version 8 wurde jedoch der static-Parameter erforderlich, um eine korrekte Migration zu gewährleisten. Die Änderungen sahen wie folgt aus:
Angular Ivy konnte in vielen Fällen automatisch die beste Wahl für den static-Parameter treffen. Bei eingebetteten Views, die durch strukturelle Direktiven erzeugt wurden, wurde der Wert static: false verwendet, was eine dynamische Abfrage ermöglichte. In Angular Ivy Version 9 wurde dieser Parameter optional, aber der Standardwert wurde auf false gesetzt. Dadurch entfielen explizite Angaben für dynamische Abfragen, was den Code vereinfacht:
Wichtig zu beachten ist, dass Query-Listen nicht von diesen historischen Änderungen betroffen sind, da sie immer dynamisch sind. Vor der Migration auf Angular Ivy Version 9 sollten Entwickler ihre Content- und View-Abfragen genau überprüfen und sicherstellen, dass keine unerwünschten Änderungen auftreten. Es empfiehlt sich, die offiziellen Migrationsanleitungen von Angular zu Rate zu ziehen, um sicherzustellen, dass alle notwendigen Anpassungen korrekt vorgenommen werden.
Ein weiterer bedeutsamer Punkt in der Migration zu Angular Ivy betrifft den Wechsel von async zu waitForAsync in Unit-Tests. Diese Änderung wurde eingeführt, um Verwirrung mit der async-await-Syntax zu vermeiden. Der waitForAsync-Wrapper stellt sicher, dass alle Micro- und Macrotasks abgeschlossen sind, bevor der Testfall als abgeschlossen markiert wird. Diese Änderung ähnelt dem klassischen done-Callback von Jasmine und Jest, aber sie bietet eine klarere und präzisere Handhabung der Asynchronität in Tests.
Besonders relevant ist auch die Migration von fehlenden @Injectable-Dekoratoren und unvollständigen Provider-Definitionen. In Angular Version 9 fügt die Migration automatisch den @Injectable-Dekorator zu Klassen hinzu, die als Provider in einem Modul registriert sind, aber keinen Dekorator besitzen. Dies betrifft besonders Service-Klassen, die ohne den Dekorator nicht korrekt von Angular injiziert werden können. Ein einfaches Beispiel zeigt, wie diese Änderung vorgenommen wird:
Ohne den @Injectable-Dekorator würde der Code nicht korrekt funktionieren, da Angular den Dienst nicht ordnungsgemäß instanziieren könnte. Die Migration stellt sicher, dass solche Probleme automatisch behoben werden, indem fehlende Dekoratoren hinzugefügt und unvollständige Provider-Definitionen korrigiert werden.
Ein weiteres bedeutendes Element der Migration betrifft die Standardkonfiguration der Angular CLI. Ab Version 12 wird die Produktionskonfiguration nun standardmäßig verwendet. Dies bedeutet, dass der --configuration=production-Parameter bei der Ausführung von ng build nicht mehr explizit angegeben werden muss. Entwickler, die mit älteren Projekten arbeiten, müssen diese Einstellung jedoch manuell aktualisieren, um die Vorteile der neuen Standardkonfiguration zu nutzen. Die Migration auf production-by-default stellt sicher, dass Projekte von Anfang an mit der optimalen Build-Konfiguration arbeiten.
Neben diesen wichtigen automatisierten Migrationen gibt es auch manuelle Anpassungen, die Entwickler vornehmen können, um ihre Angular Ivy-Anwendungen weiter zu optimieren. Eine dieser Anpassungen betrifft die Initialnavigation. In Angular Ivy Version 11 wurden die alten Werte für die initialNavigation-Option des RouterModule entfernt und durch neue, klarer definierte Werte ersetzt. Diese neuen Werte – enabledBlocking und enabledNonBlocking – ermöglichen eine präzisere Steuerung darüber, wann und wie die Initialnavigation in einer Angular-Anwendung erfolgt. enabledBlocking sorgt dafür, dass die Navigation abgeschlossen ist, bevor der Hauptkomponenten-Start erfolgt, was vor allem bei serverseitigem Rendering (Angular Universal) nützlich ist. Der Standardwert enabledNonBlocking lässt die Root-Komponente schon vor der Initialnavigation starten, was die Performance in vielen Fällen verbessert.
Die Optimierung der Change-Detection ist ein weiteres wichtiges Thema bei der Migration. Angular Ivy führt zusätzliche Optionen zur Konfiguration von NgZone ein, die eine feinere Kontrolle über den Change-Detection-Prozess ermöglichen. Zwei neue Optionen – ngZoneEventCoalescing und ngZoneRunCoalescing – bieten eine effiziente Möglichkeit, mehrere Change-Detection-Anfragen innerhalb einer einzigen VM-Drehung zu bündeln und mit der aktuellen Frame-Rate zu synchronisieren. Diese Optionen sind besonders nützlich in Anwendungen mit hoher Interaktivität, da sie die Performance deutlich verbessern können.
Zusammenfassend lässt sich sagen, dass die Migration von Angular View Engine zu Angular Ivy viele neue Möglichkeiten für eine verbesserte Performance, bessere Typensicherheit und eine vereinfachte Handhabung von Asynchronität in Unit-Tests bietet. Allerdings ist es entscheidend, alle relevanten Migrationen – sowohl automatisierte als auch manuelle – sorgfältig durchzuführen, um sicherzustellen, dass die Anwendung mit den neuesten Best Practices von Angular kompatibel bleibt und optimal funktioniert.
Wie haben außergewöhnliche Frauen die moderne Wissenschaft und Medizin verändert?
Wie man einzigartige Ohrringe aus Draht und Perlen herstellt: Ein Schritt-für-Schritt Leitfaden für kreative Schmuckgestaltung
Wie man sich in einer fremden Stadt zurechtfindet: Wichtige Ausdrücke und kulturelle Tipps
Wie bewahren Regierungsbehörden neutrale Kompetenz bei politisch sensiblen Aufgaben?
Wie kann ich emotionale Spannungen im Körper sicher und wirksam lösen?
Wie beeinflussen Blende, Brennweite und Belichtung die Schärfentiefe in der Fotografie?
Wie bringt man einem Hund bei, durch Reifen und über den Rücken zu springen?
Wie man die Elektronik für die Raygun Pen zusammenstellt und verbessert
Wie das Wildwestleben sich zwischen Humor, Härte und Legenden entfaltet
Wie die Entwicklung von Verbrechensbekämpfung und Kriminaltechnologie die Gesellschaft beeinflusst hat

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский