Winkeldienste und Abhängigkeitsinjektion erklärt

Dienstleistungen und Injektoren

Komponenten sind für die Daten verantwortlich, die in die Vorlage gerendert werden. Durch externe Dienste kann diese Verantwortung vereinfacht werden. Darüber hinaus ist die Einkapselung von Fremdkörpern viel einfacher zu warten.

Das Delegieren zu vieler Verantwortlichkeiten auf eine einzelne Komponente kann die Komponentenklasse komplizieren. Und was ist, wenn diese Verantwortlichkeiten auf mehrere Komponenten zutreffen? Das Kopieren und Einfügen einer solchen Logik ist äußerst schlecht. Zukünftige Änderungen an der Logik wären schwieriger zu implementieren und zu testen.

Angular wollte dieses Problem mit Diensten und Abhängigkeitsinjektion eindämmen. Beide Konzepte arbeiten zusammen, um modulare Funktionen bereitzustellen .

Komponenten müssen auch keine fremden Informationen bereitstellen. Ein Dienst importiert das, was er benötigt, um im Namen der von ihm bedienten Komponenten zu funktionieren . Die Komponenten müssen nur den Service instanziieren. Von dort aus bedienen sie ihre eigenen Bedürfnisse mit der instanziierten Dienstinstanz.

Beim Testen und zukünftigen Ändern befindet sich die gesamte Logik an einem Ort. Der Dienst wird von seiner Quelle aus instanziiert. Tests und Änderungen an der Quelle gelten überall dort, wo der Dienst injiziert wird.

Einführung in Services

Ein Dienst ist eine Art Schaltplan , der in Angular verfügbar ist. Es kann über die Befehlszeilenschnittstelle (CLI) generiert werden : ng generate service [name-of-service]. Ersetzen Sie [name-of-service]durch einen bevorzugten Namen. Der CLI-Befehl liefert Folgendes.

import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class LoggerService { constructor() { } }

Die Logik eines Dienstes unterscheidet sich innerhalb seiner Klasse. Angular interpretiert eine Klasse als injizierbaren Dienst, der auf dem @InjectableDekorateur basiert . Injizierbare Dienste müssen sich bei einem Injektor registrieren lassen .

Die Komponente instanziiert einen Dienst, während der Injektor diese Instanz bereitstellt. Lesen Sie weiter im nächsten Abschnitt, um mehr über Injektoren zu erfahren.

Das @InjectableMetadatenfeld providedIn: ‘root’zielt auf das Stammmodul der aktuellen Anwendung ab ( app.module.ts). Es registriert den Dienst beim Injektor des Moduls, so dass es diesen Dienst in eines seiner Kinder injizieren kann .

Injektoren sind die Bausteine ​​des Abhängigkeitsinjektionssystems von Angular. Injektoren sind ein guter Ort, um Ihre Aufmerksamkeit zu konzentrieren, bevor Sie mit den Dienstleistungen fortfahren.

Injektoren

Eine Anwendung enthält, beginnend mit app.module.ts, eine Hierarchie von Injektoren. Sie existieren neben jedem Modul und jeder Komponente im Anwendungsbaum.

Anwendungshierarchie

Die grünen Kreise zeigen Injektoren an. Sie bieten Service-Instanzen zum Instanziieren von Komponenten. Je nachdem, bei welchem ​​Injektor ein Service registriert ist, kann er für eine Komponente verfügbar sein oder nicht.

Im Stammverzeichnis der App ( app.module.ts) registrierte Dienste stehen allen Komponenten zur Verfügung. Bei einem Injektor für eine Komponente ist möglicherweise kein bestimmter Service registriert. Wenn dies der Fall ist und die Komponente ihre Instanziierung anfordert, verschiebt sich der Injektor zu seinem übergeordneten Element. Dieser Trend setzt sich fort, bis entweder der Wurzelinjektor erreicht ist oder der Dienst gefunden wurde.

Wenn Sie sich das Diagramm ansehen, sagen Sie, dass sich ein Dienst am Injektor von Punkt B registriert. Alle Komponenten an Punkt C und unten können nicht auf den am Injektor von B registrierten Service zugreifen. Injektoren werden ihren Kindern niemals eine Service-Instanz vorenthalten.

Abhängigkeitsspritze

Es gibt mehrere Möglichkeiten, einen Dienst bei den Injektoren einer Anwendung zu registrieren.

Das providedIn: ‘root’Metadatenfeld von @Injectablebietet den am meisten empfohlenen Ansatz. Dieses Metadatenfeld wurde mit Angular 6 veröffentlicht.

Wie bereits erwähnt, wird providedIn: ‘root’ein Dienst beim Root-Modul-Injektor registriert. Dadurch ist es für die gesamte Anwendung sofort instanziierbar.

Die Neuheit von providedIn: ‘root’ist Baumschütteln . Wenn der Dienst trotz seiner Registrierung nicht benutzt wird, wird es geschüttelt von der Anwendung zur Laufzeit. Auf diese Weise werden keine Ressourcen verbraucht.

Die anderen beiden Wege sind direkter und traditioneller. Zugegeben, sie bieten kein Baumschütteln an.

Ein Dienst kann sich bei jedem Injektor entlang des Komponentenbaums registrieren. Sie fügen den Dienst als Anbieter in das @ComponentMetadatenfeld ein : providers: []. Der Dienst steht der Komponente und ihren untergeordneten Elementen zur Verfügung

In der dritten Registrierungsstrategie providers: []existieren die Metadaten als eigenes Feld im @NgModuleDekorator. Der Service kann vom Modul zum zugrunde liegenden Komponentenbaum instanziiert werden.

Beachten Sie, dass im Gegensatz zu providedIn: ‘root’, @NgModuleAnmeldung nicht tree-Schütteln. Beide Strategien sind ansonsten identisch. Sobald sich ein Dienst bei registriert @NgModule, verbraucht er Ressourcen, auch wenn er von der Anwendung nicht verwendet wird.

Dienstleistungen fortgesetzt

Als nächstes kommt das Schreiben eines tatsächlichen Dienstes. Zusammenfassend lässt sich sagen, dass Dienste bestimmte Funktionen im Namen der Komponenten einer Anwendung ausführen.

Services zeichnen sich durch die Abwicklung gängiger Vorgänge aus. Sie ersparen den Bauteilen damit die Verantwortung. Dies spart Zeit, da gemeinsame Vorgänge nicht über mehrere Komponenten hinweg neu geschrieben werden müssen. Es ist auch besser testbar, da sich der Code an einer Stelle befindet. Änderungen müssen nur an einem Ort vorgenommen werden, ohne dass an anderer Stelle gesucht werden muss.

Anwendungsfälle

Ein paar Beispiele tragen wesentlich zum vollständigen Verständnis der Dienstleistungen bei.

  • Konsolenprotokolle
  • API-Anfragen

Beides ist in den meisten Anwendungen gleich. Durch Services zur Abwicklung dieser Vorgänge wird die Komplexität der Komponenten verringert.

Konsolenprotokolle

Dieses Beispiel baut auf dem @InjectableBasisskelett auf. Das Skelett ist durch Ausführen der CLI ( ng generate service [name-of-service]]) verfügbar .

// services/logger.service.ts import { Injectable } from '@angular/core'; interface LogMessage { message:string; timestamp:Date; } @Injectable({ providedIn: 'root' }) export class LoggerService { callStack:LogMessage[] = []; constructor() { } addLog(message:string):void { // prepend new log to bottom of stack this.callStack = [{ message, timestamp: new Date() }].concat(this.callStack); } clear():void { // clear stack this.callStack = []; } printHead():void  printLog():void { // print bottom to top of stack on screen this.callStack.reverse().forEach((logMessage) => console.log(logMessage)); } getLog():LogMessage[] { // return the entire log as an array return this.callStack.reverse(); } }

LoggerService registriert sich über die @InjectableMetadaten beim Root-Modul . Somit kann es in der instanziieren app.component.html.

// app.component.ts import { Component, OnInit } from '@angular/core'; import { LoggerService } from './services/logger.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent implements OnInit { logs:object[] = []; constructor(private logger:LoggerService) { } updateLog():void { this.logger.printHead(); this.logs = this.logger.getLog(); } logMessage(event:any, message:string):void { event.preventDefault(); this.logger.addLog(`Message: ${message}`); this.updateLog(); } clearLog():void { this.logger.clear(); this.logs = []; } ngOnInit():void { this.logger.addLog(“View Initialized”); this.updateLog(); } }

Die HTML-Vorlage bietet weitere Einblicke in die Verwendung von LoggerService durch die Komponente.

Log Example

SUBMIT

Complete Log

CLEAR
  • {{ logs.length - i }} > {{ log.message }} @ {{ log.timestamp }}

Dies hat das Gefühl einer ToDo-Anwendung. Sie können Nachrichten protokollieren und das Nachrichtenprotokoll löschen. Stellen Sie sich vor, die gesamte Logik des Dienstes wurde in AppComponent verschoben! Es hätte den Code kompliziert gemacht. LoggerService hält den protokollbezogenen Code aus der AppComponent-Kernklasse gekapselt.

Anfragen abrufen

Here is one more example worth playing around with. This example is possible thanks to typicode’s JSONPlaceholder1. The API is public and free to use.

import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; // //jsonplaceholder.typicode.com // public API created by typicode @ //github.com/typicode interface Post { userId:number; id:number; title:string; body:string; } @Injectable({ providedIn: 'root' }) export class PlaceholderService { constructor(private http:HttpClient) { } getPosts():Observable { return this.http.get('//jsonplaceholder.typicode.com/posts'); } getPost(id:number):Observable { return this.http.get(`//jsonplaceholder.typicode.com/posts/${id}`); } }

This is more of a stand-alone piece than a fully fleshed out example. Fetch requests tend to work better as an injectable service. The alternative is an over-complicated component. The injected class subscribes to what the PlaceholderService pre-configures.

Conclusion

Services and dependency injection are very useful together. They allow developers to encapsulate common logic and inject across multiple different components. This alone is a massive convenience for any future maintenance.

Injectors work as intermediaries. They mediate between instantiating components and a reservoir of registered services. Injectors offer these instantiable services to their branch children.

Weitere Informationen zu Diensten und zur Abhängigkeitsinjektion finden Sie unter den nächsten Links.

Ressourcen für Angular

  • Winkeldokumentation
  • In der Einführung in die Winkelabhängigkeitsinjektion
  • Was ist Abhängigkeitsinjektion und wann wird sie verwendet?
  • Beste Angular-Codebeispiele
  • Angular GitHub Repository
  • Abhängigkeitsspritze
  • Einführung in Services und DI