Dependency Injection
Dependency Injection ist ein weiteres Konzept, das nicht nur für Angular gilt, sondern ein allgemeines Pattern ist. Im Zusammenhang mit Angular bedeutet dies, dass wir keine Instanzen von Klassen selbst erstellen, z. B.:
export class AppComponent {
myService = new MyService();
}
Angular übernimmt die Erstellung neuer Instanzen unserer Klassen und stellt sie uns über Dependency Injection zur Verfügung, was wie folgt aussieht:
export class AppComponent {
private readonly myService = inject(MyService);
}
Angular verwendet hier den Typ MyService
als InjectionToken
. Auf diese Weise weiss Angular, dass wir eine Instanz von MyService
benötigen. Die Instanz von MyService
wird myService
zugewiesen, sodass wir sie verwenden können.
Warum Dependency Injection?
Warum sollte man dies in Angular tun? Warum nicht einfach selbst neue Instanzen erstellen? Einige Vorteile sind:
Es erleichtert automatisierte Tests, da wir leicht gefälschte Versionen von Abhängigkeiten anstelle von echten bereitstellen können.
Es ermöglicht uns, Instanzen unserer Objekte einfacher zu teilen. Auf diese Weise können zwei separate Komponenten dieselbe Instanz eines Dienstes gemeinsam nutzen, was bedeutet, dass sie Daten austauschen können, oder wir könnten zwei separate Instanzen des Dienstes erstellen, wenn wir dies bevorzugen.
Es erleichtert die Erstellung von Instanzen erheblich, da wir nicht alle Abhängigkeiten übergeben müssen, von denen der Dienst selbst abhängt.
Tokens und Providers
Wir haben bereits gesehen, wie Dependency Injection funktioniert:
export class AppComponent {
private readonly myService = inject(MyService);
}
Es scheint, als würden wir einfach ein Argument unserer inject
-Funktion übergeben, den Angular für uns injizieren soll. Wenn wir ihm den Typ MyService
zuweisen, erhalten wir eine Instanz von MyService
.
Was wir hier wirklich tun, ist die Bereitstellung eines Typ-Tokens (wir könnten auch String-Tokens verwenden, aber das ist nicht so üblich). Du denkst vielleicht, dass Angular weiss, was MyService
ist, weil wir das am Anfang unserer Datei importieren würden:
import { MyService } from './shared/data-access/my-service';
export class AppComponent {
private readonly myService = inject(MyService);
}
Es erscheint sinnvoll anzunehmen, dass Angular einfach MyService
aus dieser Datei sucht und eine neue Instanz davon erstellt, aber so funktioniert es nicht ganz. Dieser Typ wird als Token verwendet, und was dieses Token darstellt, hängt davon ab, wie es bereitgestellt wird.
Tokens bereitstellen
Ein Aspekt von Komponenten, den wir noch nicht betrachtet haben, ist das providers
-Array, und dieser ist für die Bereitstellung von Tokens relevant. Zunächst einmal ist die manuelle Bereitstellung von Tokens bei weitem nicht so verbreitet wie die folgende Vorgehensweise:
@Injectable({
providedIn: 'root'
})
export class MyService {}
Dadurch wird das MyService
-Token so konfiguriert, dass es eine Instanz der MyService
-Klasse auf der Stammebene der Anwendung darstellt. Das bedeutet, dass jeder Teil der Anwendung, der MyService
injiziert, dieselbe einzelne Instanz davon gemeinsam nutzt. So verfahren wir in den meisten Fällen.
Nehmen wir an, wir möchten, dass eine unserer Komponenten eine eigene Instanz von MyService
hat. Dies könnten wir erreichen, indem wir das providers
-Array in unseren Komponenten-Metadaten verwenden:
@Component({
selector: 'app-my-component',
template: `<p>hello</p>`,
providers: [MyService]
})
export class MyComponent {}
Selbst wenn wir unseren MyService
in der Root bereitstellen, überschreibt unsere Komponente dies, um eine eigene Instanz zu erstellen. Wir könnten diesen Service auch nicht in der Root bereitstellen und würden ihn nur dort verwenden können, wo wir die Providers manuell definieren:
@Injectable()
export class MyService {}
Kurz gesagt: Verwende providedIn: 'root'
, wenn du eine einzelne Instanz des Dienstes für Ihre gesamte Anwendung freigeben möchten. Wenn Sie möchten, dass eine Komponente über eine eigene Instanz des Dienstes verfügt, übergeben Sie diese an das providers-Array dieser Komponente.
Tokens überschreiben
Dies wird weitaus seltener verwendet und dient hauptsächlich dazu, Abhängigkeiten in automatisierten Tests zu simulieren, aber mit Tokens lassen sich noch weitere Dinge tun. Ein Token muss nicht unbedingt das sein, woraus sein Typ abgeleitet wurde. Beispielsweise muss MyService
keine Instanz von MyService
sein.
Bei der Bereitstellung eines Tokens können wir überschreiben, was es tatsächlich ist:
providers: [{ provide: MyService, useClass: CoolService }]
Jetzt sagen wir, dass wir das Token MyService
bereitstellen möchten, aber wenn dieses Token verwendet wird, möchten wir stattdessen eigentlich eine Instanz von CoolService
verwenden.
Zuletzt aktualisiert