Winkelabhängigkeitsinjektion anhand von Beispielen erläutert

Was ist Abhängigkeitsinjektion?

Motivation

Die Abhängigkeitsinjektion wird oft einfacher als DI bezeichnet. Das Paradigma existiert in ganz Angular. Es hält Code flexibel, testbar und veränderbar. Klassen können externe Logik erben, ohne zu wissen, wie sie erstellt wird. Verbraucher dieser Klassen müssen ebenfalls nichts wissen.

DI erspart Klassen und Verbrauchern, mehr als nötig zu wissen. Dank der Mechanismen, die DI in Angular unterstützen, ist der Code jedoch so modular wie zuvor.

Dienstleistungen sind ein wesentlicher Wohltäter von DI. Sie stützen sich auf das Paradigma für die Injektion in verschiedene Verbraucher. Diese Verbraucher können dann die von diesem Dienst bereitgestellten Dienste nutzen und / oder an eine andere Stelle weiterleiten.

Service sind nicht allein. Richtlinien, Rohre, Komponenten usw.: Jeder Schaltplan in Angular profitiert auf die eine oder andere Weise von DI.

Injektoren

Injektoren sind Datenstrukturen, in denen Anweisungen gespeichert sind, in denen angegeben ist, wo und wie Dienste gebildet werden. Sie fungieren als Vermittler innerhalb des Angular DI-Systems.

Modul-, Direktiven- und Komponentenklassen enthalten Metadaten, die für Injektoren spezifisch sind. Jede dieser Klassen wird von einer neuen Injektorinstanz begleitet. Auf diese Weise spiegelt der Anwendungsbaum seine Hierarchie von Injektoren wider.

Die providers: []Metadaten akzeptieren Dienste, die sich dann beim Injektor der Klasse registrieren. In diesem Anbieterfeld werden die Anweisungen hinzugefügt, die für die Funktion eines Injektors erforderlich sind. Eine Klasse (vorausgesetzt, sie hat Abhängigkeiten) instanziiert einen Dienst, indem sie ihre Klasse als Datentyp übernimmt. Der Injektor richtet diesen Typ aus und erstellt eine Instanz dieses Dienstes im Namen der Klasse.

Natürlich kann die Klasse nur instanziieren, wofür der Injektor Anweisungen hat. Wenn für den eigenen Injektor der Klasse der Dienst nicht registriert ist, fragt er den übergeordneten Dienst ab. So weiter und so fort, bis entweder ein Injektor mit dem Dienst oder dem Anwendungsstamm erreicht wird.

Services können an jedem Injektor in der Anwendung registriert werden. Services werden in das providers: []Metadatenfeld von Klassenmodulen, Direktiven oder Komponenten aufgenommen. Die Klasse "Kinder können einen im Injektor der Klasse registrierten Dienst instanziieren". Kinderinjektoren greifen schließlich auf Elterninjektoren zurück.

Abhängigkeitsspritze

Schauen Sie sich die Skelette für jede Klasse an: Service, Modul, Direktive und Komponente.

// service import { Injectable } from '@angular/core'; @Injectable({ providedIn: /* injector goes here */ }) export class TemplateService { constructor() { } }
// module import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ imports: [ CommonModule ], declarations: [], providers: [ /* services go here */ ] }) export class TemplateModule { }
// directive import { Directive } from '@angular/core'; @Directive({ selector: '[appTemplate]', providers: [ /* services go here */ ] }) export class TemplateDirective { constructor() { } }
//component import { Component } from '@angular/core'; @Component({ selector: 'app-template', templateUrl: './template.component.html', styleUrls: ['./template.component.css'], providers: [ /* services go here */ ] }) export class TemplateComponent { // class logic ... }

Jedes Skelett kann Dienste für einen Injektor registrieren. In der Tat, TemplateService ist eine Dienstleistung. Ab Angular 6 können sich Dienste jetzt mithilfe von @InjectableMetadaten bei Injektoren registrieren .

Auf jeden Fall

Beachten Sie die Metadaten providedIn: string( @Injectable) und providers: []( @Directive, @Componetund @Module). Sie teilen den Injektoren mit, wo und wie ein Service erstellt werden soll. Andernfalls würden die Injektoren nicht wissen, wie sie instanziieren sollen.

Was ist, wenn ein Dienst Abhängigkeiten aufweist? Wohin würden die Ergebnisse gehen? Die Anbieter beantworten diese Frage, damit die Injektoren ordnungsgemäß instanziieren können.

Injektoren bilden das Rückgrat des DI-Frameworks. Sie speichern Anweisungen zum Instanziieren von Diensten, damit Verbraucher dies nicht tun müssen. Sie erhalten Dienstinstanzen, ohne etwas über die Quellabhängigkeit wissen zu müssen!

Ich sollte auch beachten, dass andere Schaltpläne ohne Injektoren immer noch die Abhängigkeitsinjektion verwenden können. Sie können keine zusätzlichen Dienste registrieren, aber sie können trotzdem von Injektoren instanziieren.

Bedienung

Die providedIn: stringMetadaten von geben @Injectablean, bei welchem ​​Injektor registriert werden soll. Bei Verwendung dieser Methode und je nachdem, ob der Dienst verwendet wird, kann sich der Dienst beim Injektor registrieren oder nicht. Angular nennt das Baumschütteln .

Standardmäßig ist der Wert auf eingestellt ‘root’. Dies führt zum Root-Injektor der Anwendung. Wenn Sie das Feld so einstellen, ‘root’dass der Dienst überall verfügbar ist.

Schnelle Notiz

Wie bereits erwähnt, greifen Kinderinjektoren auf ihre Eltern zurück. Diese Fallback-Strategie stellt sicher, dass sich Eltern nicht für jeden Injektor neu registrieren müssen. In diesem Artikel zu Services und Injektoren finden Sie eine Illustration dieses Konzepts.

Registrierte Dienste sind Singletons . Das heißt, die Anweisungen zum Instanziieren des Dienstes sind nur auf einem Injektor vorhanden. Dies setzt voraus, dass es nicht ausdrücklich an anderer Stelle registriert wurde.

Modul, Richtlinie und Komponente

Module und Komponenten haben jeweils eine eigene Injektorinstanz. Dies ist angesichts des providers: []Metadatenfeldes offensichtlich . Dieses Feld nimmt ein Array von Diensten und registriert sie beim Injektor des Moduls oder der Komponentenklasse. Dieser Ansatz kommt in dem @NgModule, @Directiveoder @ComponentDekorateure.

Diese Strategie verhindert das Schütteln von Bäumen oder das optionale Entfernen nicht verwendeter Dienste von Injektoren. Service-Instanzen leben während der gesamten Lebensdauer des Moduls oder der Komponente auf ihren Injektoren.

Verweisen von Referenzen

Verweise auf das DOM können von jeder Klasse aus instanziiert werden. Beachten Sie, dass Referenzen weiterhin Dienste sind. Sie unterscheiden sich von traditionellen Diensten darin, dass sie den Zustand von etwas anderem darstellen. Diese Dienste enthalten Funktionen zur Interaktion mit ihrer Referenz.

Richtlinien benötigen ständig DOM-Referenzen. Direktiven führen durch diese Referenzen Mutationen an ihren Wirtselementen durch. Siehe folgendes Beispiel. Der Injektor der Direktive instanziiert eine Referenz des Host-Elements in den Konstruktor der Klasse.

// directives/highlight.directive.ts import { Directive, ElementRef, Renderer2, Input } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor( private renderer: Renderer2, private host: ElementRef ) { } @Input() set appHighlight (color: string) { this.renderer.setStyle(this.host.nativeElement, 'background-color', color); } }
// app.component.html 

Highlighted Text!

Renderer2wird auch instanziiert. Von welchem ​​Injektor kommen diese Dienstleistungen? Nun, der Quellcode jedes Dienstes stammt von @angular/core. Diese Dienste müssen sich dann beim Root-Injektor der Anwendung registrieren.

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { HighlightDirective } from './directives/highlight.directive'; @NgModule({ declarations: [ AppComponent, HighlightDirective ], imports: [ BrowserModule ], providers: [], bootstrap: [ AppComponent ] }) export class AppModule { }

Ein leeres Provider-Array!? Nicht zu fürchten. Angular registriert viele Dienste automatisch beim Root-Injektor. Dies beinhaltet ElementRefund Renderer2. In diesem Beispiel verwalten wir das Host-Element über seine Schnittstelle, die aus der Instanziierung von stammt ElementRef. Renderer2Lassen Sie uns das DOM über das Ansichtsmodell von Angular aktualisieren.

You can read more about views from this article. They are the preferred method for DOM/view updates in Angular applications.

It is important recognize the role that injectors play in the above example. By declaring variable types in the constructor, the class obtains valuable services. Each parameter’s data type maps to a set of instructions within the injector. If the injector has that type, it returns an instance of said type.

Instantiating Services

The Services and Injectors article explains this section to an extent. Though, this section rehashes the previous section or the most part. Services will often provide references to something else. They may just as well provide an interface extending a class’ capabilities.

The next example will define a logging service that gets added to a component’s injector via its providers: [] metadata.

// services/logger.service.ts import { Injectable } from '@angular/core'; @Injectable() export class LoggerService { callStack: string[] = []; addLog(message: string): void { this.callStack = [message].concat(this.callStack); this.printHead(); } clear(): void { this.printLog(); this.callStack = []; console.log(“DELETED LOG”); } private printHead(): void  null);  private printLog(): void { this.callStack.reverse().forEach((log) => console.log(message)); } }
// app.component.ts import { Component } from '@angular/core'; import { LoggerService } from './services/logger.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', providers: [LoggerService] }) export class AppComponent { constructor(private logger: LoggerService) { } logMessage(event: any, message: string): void { event.preventDefault(); this.logger.addLog(`Message: ${message}`); } clearLog(): void { this.logger.clear(); } }
// app.component.html 

Log Example

SUBMIT

Delete Logged Messages

CLEAR

Focus on the AppComponent constructor and metadata. The component injector receives instructions from the provider’s metadata field containing LoggerService. The injector then knows what to instantiate LoggerService from requested in the constructor.

The constructor parameter loggerService has the type LoggerService which the injector recognizes. The injector follows through with the instantiation as mentioned.

Conclusion

Dependency injection (DI) is a paradigm. The way it works in Angular is through a hierarchy of injectors. A class receives its resources without having to create or know about them. Injectors receive instruction and instantiate a service depending on which one was requested.

DI shows up a lot in Angular. The official Angular documentation explains why the paradigm is so prevalent. They also go on to describe the numerous use-cases for DI in Angular way beyond what was discussed in this article. Check it out by clicking below!

More on dependency injection:

  • Intro to Angular dependency injection
  • Quick intro to dependency injection