Dependency Injection mit Angular 9

Der Angular 9 Release, eines der grössten Updates in den letzten drei Jahren, ist bereits knapp vier Monate her. Der Schwerpunkt vom Release und somit auch von vielen der Release Notes lag sicherlich auf dem Ivy-Compiler. Dieser ist nun standardmässig in Angular aktiviert. Ivy wurde bereits mit Angular 8 als Opt-In-Möglichkeit eingeführt und in einem früheren Blog-Artikel erläutert.

Auch wenn die Änderungen mit Ivy sehr umfangreich sind, ist dies jedoch nicht das einzige neue Feature, welches mit dem Angular 9 Release eingeführt wurde. Denn es wurden zum Beispiel auch die Möglichkeiten der Dependency Injection erweitert mit zwei neuen Optionen für providedIn. Dies wurde jedoch im Release Note nur kurz erläutert. Aufgrund dessen wird in diesem Blog-Artikel ein Überblick über die bisherigen und die nun erweiterten Möglichkeiten der Dependency Injection in Angular gegeben.

Ein bisschen Geschichte

Das Attribut providedIn wird für das Dependency Injection Design Pattern verwendet. Hierfür hat Angular ein eigenes DI Framework. Dabei werden Services (Klassen, welche Dienste und Methoden beinhalten, um gewisse Aufgaben in der Applikation auszuführen) als Abhängigkeiten in die Module injiziert.

Vor Angular 6 bestand die einzige Möglichkeit der Dependency Injection darin, die Services explizit in der Klasse des Moduls zu importieren und via dem @NgModule-Decorator zu registrieren:

@NgModule({
  providers: [MyService]
})
export class MyModule { }

Für eagerly loaded modules (Module, welche vor dem Applikationsstart geladen werden) wird der Service als Singleton zur Verfügung gestellt. Diese eine Instanz, die vom Injektor kreiert wird, wird bei den root level injectors registriert.

Lazy loaded modules (Module, welche asynchron und nur bei Verwendung geladen werden) haben einen eigenen root scope. Die Erstellung des Services zur Laufzeit durch den child injector wird dann also im child scope der lazy loaded modules registriert, und nicht im app root scope. Aufgrund dessen kann es dazu führen, dass ein Service unter Umständen mehrere Male instanziiert wird und somit kein Singleton mehr ist.

Ein Nachteil dieses Vorgehens mit dem @NgModule-Decorator besteht auch darin, dass kein «Tree-shaking» unterstützt wird. Tree-shaking bedeutet, dass der Angular Compiler unbenutzte Funktionen vom Production Build entfernt. Üblicherweise werden selbst geschriebene Services und dessen Funktionen verwendet – jedoch gerade beim Import von fremden Libraries kann Tree-shaking von Vorteil sein. Da von fremden Libraries oftmals nicht jede der verfügbaren Funktionen eines Services benutzt wird, können alle nicht verwendeten Funktionen vom Production Build entfernt werden. Somit hilft Tree-shaking dabei, die Applikation kleiner und schneller zu machen.

Mit Angular 6 wurde dann ein neues Feature implementiert: Die Tree-Shakable Providers. Mit diesem Feature kann man den Provider direkt beim Service selbst via dem @Injectable-Decorator registrieren. Dem Attribut providedIn konnte dann entweder der Wert ‘root’ (die Auswirkungen davon wird im späteren Absatz ausführlicher erläutert) oder die selbst erstellen Module der Applikation übergeben werden:

@Injectable({
  providedIn: 'root'
})
export class MyService { }

oder

@Injectable({
  providedIn: MyModule
})
export class MyService { }

Ein Vorteil gegenüber dem vorherigen Vorgehen mit dem @NgModule-Decorator ist, wie schon angedeutet, dass hierbei das Tree-shaking ermöglicht wird.

Mit Angular 9 sind nun zwei neue Werte für das Attribut providedIn implementiert worden: ‘any’ und ‘platform’. Somit kann mit providedIn zurzeit die folgenden Werte verwendet werden:

Werte für providedIn und dessen Auswirkungen

‘root’

Dies ist der Wert, der standardmässig eingesetzt wird. Wird ein Service via dem Angular CLI generiert (ng generate service myservice), wird dem erstellten Service diese Variante eingeschrieben. ‘root’ ist ein Alias für AppModule (und somit root injector).

Der Service wird somit auf root level registriert und als globaler Singleton zur Verfügung gestellt. Demnach kann der Service überall in der Applikation injiziert werden. Dies bedeutet auch, dass lazy loaded modules diesen globalen Singleton vom root scope verwenden, und nicht eine eigene Instanz vom eigenen child scope.

‘platform’

Dieser Wert ermöglicht ein ähnliches Verhalten wie ‘root’: Es erzeugt ebenfalls einen Singleton Service, welcher auch von lazy loaded modules verwendet wird. Der Unterschied zu ‘root’ wird sichtbar, wenn verschiedene Applikationen auf einer Seite geladen werden. Dieser Singleton Service wird dann auf platform scope allen Applikationen zur Verfügung gestellt. Die Applikationen teilen sich somit den platform injector, welcher sich eine Ebene über root befindet. Die einzelnen Applikationen verfügen weiterhin über weitere, eigene Services via root injector.

Ein Anwendungsfall für den Wert ‘platform’ wäre also, wenn ein Service über Applikationsgrenzen hinweg geteilt werden soll. Diese Option kann zum Beispiel bei Angular Elements verwendet werden, welche sich wie Mini-Angular Applikationen verhalten. Ebenfalls kann es sich bei der zurzeit trendigen MonoRepo-Struktur als nützlicher Ansatz erweisen.

‘any’

Services, die mit dem Wert ‘any’ zur Verfügung gestellt werden, werden für jedes Modul, das den Service benutzt, als eine isolierte Instanz erzeugt. Die eagerly loaded modules teilen sich zwar nach wie vor eine Instanz, welche beim root injector registriert ist. Aber bei den lazy loaded modules hingegen erhält jedes Modul seine eigene Instanz des Services. Somit wird dieser Service nicht mehr als globaler Singleton geführt.

Zusammenfassung

Abschliessend kann gesagt werden, dass mit dem @Injectable-Decorator tree-shakable Services als Dependencies injiziert werden können. Dabei wird ‘root’ standardmässig für die meisten Service benutzt werden. Aber auch die Werte ‘platform’ und ‘any’, welche mit Angular 9 eingeführt wurden, bieten sich je nach Anwendungsfall als gute Alternativen an.

Views All Time
1485
Views Today
4
Short URL for this post: https://blog.oio.de/Lmjwe
This entry was posted in Web as a Platform and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *