Einige eckige Tipps

Seit der Veröffentlichung des aktualisierten Angulars ist genug Zeit vergangen. Derzeit sind viele Projekte abgeschlossen. Viele Entwickler haben bereits von Anfang an auf die sinnvolle Verwendung dieses Frameworks und seiner Funktionen umgestellt und gelernt, wie man Fallstricke umgeht. Jeder Entwickler und / oder jedes Team hat entweder bereits seine eigenen Styleguides und Best Practices erstellt oder verwendet andere. Gleichzeitig müssen Sie sich jedoch häufig mit viel Angular-Code befassen, der nicht viele der Funktionen dieses Frameworks verwendet und / oder im Stil von AngularJS geschrieben ist.


Dieser Artikel enthält einige der Funktionen und Merkmale der Verwendung des Angular-Frameworks, die nach der bescheidenen Meinung des Autors in den Handbüchern nicht ausreichend behandelt werden oder von den Entwicklern nicht verwendet werden. Der Artikel beschreibt die Verwendung von HTTP-Anforderungen "Interceptors" und die Verwendung von Route Guards, um den Zugriff auf Benutzer zu beschränken. Einige Empfehlungen zur Verwendung von RxJS und zum Verwalten des Anwendungsstatus werden gegeben. Außerdem werden einige Empfehlungen zum Design des Projektcodes vorgestellt, die den Projektcode wahrscheinlich sauberer und verständlicher machen. Der Autor hofft, dass dieser Artikel nicht nur Entwicklern nützlich sein wird, die Angular gerade erst kennenlernen, sondern auch erfahrenen Entwicklern.


Arbeiten Sie mit HTTP


Die Erstellung einer Client-Webanwendung erfolgt anhand von HTTP-Anforderungen an den Server. In diesem Teil werden einige Funktionen des Angular-Frameworks für die Arbeit mit HTTP-Anforderungen erläutert.


Interceptors verwenden


In einigen Fällen kann es erforderlich sein, die Anforderung zu ändern, bevor sie den Server erreicht. Oder Sie müssen jede Antwort ändern. Ab Angular 4.3 wurde ein neuer HttpClient veröffentlicht. Es wurde die Möglichkeit hinzugefügt, eine Anforderung mit Interceptors abzufangen (Ja, sie wurden schließlich erst in Version 4.3 zurückgegeben! Dies war eine der am meisten erwarteten fehlenden Funktionen von AngularJs, die nicht nach Angular migriert wurden). Dies ist eine Art Middleware zwischen der http-API und der eigentlichen Anfrage.


Ein häufiger Anwendungsfall kann die Authentifizierung sein. Um eine Antwort vom Server zu erhalten, müssen Sie der Anforderung häufig einen Authentifizierungsmechanismus hinzufügen. Diese Aufgabe mit Interceptors wird ganz einfach gelöst:


import { Injectable } from "@angular/core"; import { Observable } from "rxjs/Observable"; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from @angular/common/http"; @Injectable() export class JWTInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { req = req.clone({ setHeaders: { authorization: localStorage.getItem("token") } }); return next.handle(req); } } 

Da eine Anwendung mehrere Interceptors haben kann, sind sie in einer Kette organisiert. Das erste Element wird vom Angular Framework selbst aufgerufen. Anschließend sind wir für die Übermittlung der Anfrage an den nächsten Interceptor verantwortlich. Dazu rufen wir die Handle-Methode des nächsten Elements in der Kette auf, sobald wir fertig sind. Wir verbinden den Abfangjäger:


 import { BrowserModule } from "@angular/platform-browser"; import { NgModule } from "@angular/core"; import { AppComponent } from "./app.component"; import { HttpClientModule } from "@angular/common/http"; import { HTTP_INTERCEPTORS } from "@angular/common/http"; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: JWTInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule {} 

Wie Sie sehen können, ist die Verbindung und Implementierung von Interceptors recht einfach.


Fortschrittsverfolgung


Eine der Funktionen von HttpClient ist die Möglichkeit, den Fortschritt einer Anforderung zu verfolgen. Wenn Sie beispielsweise eine große Datei herunterladen müssen, möchten Sie dem Benutzer wahrscheinlich den Fortschritt des Downloads melden. Um Fortschritte zu HttpRequest , müssen Sie die HttpRequest Eigenschaft des HttpRequest Objekts auf true . Ein Beispiel für einen Dienst, der diesen Ansatz implementiert:


 import { Observable } from "rxjs/Observable"; import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { HttpRequest } from "@angular/common/http"; import { Subject } from "rxjs/Subject"; import { HttpEventType } from "@angular/common/http"; import { HttpResponse } from "@angular/common/http"; @Injectable() export class FileUploadService { constructor(private http: HttpClient) {} public post(url: string, file: File): Observable<number> { var subject = new Subject<number>(); const req = new HttpRequest("POST", url, file, { reportProgress: true }); this.httpClient.request(req).subscribe(event => { if (event.type === HttpEventType.UploadProgress) { const percent = Math.round((100 * event.loaded) / event.total); subject.next(percent); } else if (event instanceof HttpResponse) { subject.complete(); } }); return subject.asObservable(); } } 

Die Post-Methode gibt eine Observable , die den Fortschritt des Downloads darstellt. Jetzt müssen Sie nur noch den Ladevorgang in der Komponente anzeigen.


Routing Verwenden von Route Guard


Durch das Routing können Sie Anwendungsanforderungen bestimmten Ressourcen innerhalb der Anwendung zuordnen. Sehr oft ist es notwendig, das Problem der Einschränkung der Sichtbarkeit des Pfades zu lösen, auf dem sich bestimmte Komponenten befinden, abhängig von bestimmten Bedingungen. In diesen Fällen verfügt Angular über einen Übergangsbeschränkungsmechanismus. Als Beispiel gibt es einen Dienst, der den Routenschutz implementiert. Angenommen, in einer Anwendung wird die Benutzerauthentifizierung mithilfe von JWT implementiert. Eine vereinfachte Version des Dienstes, die prüft, ob der Benutzer autorisiert ist, kann wie folgt dargestellt werden:


 @Injectable() export class AuthService { constructor(public jwtHelper: JwtHelperService) {} public isAuthenticated(): boolean { const token = localStorage.getItem("token"); //        return !this.jwtHelper.isTokenExpired(token); } } 

Um den CanActivate zu implementieren, müssen Sie die CanActivate Schnittstelle implementieren, die aus einer einzelnen canActivate Funktion besteht.


 @Injectable() export class AuthGuardService implements CanActivate { constructor(public auth: AuthService, public router: Router) {} canActivate(): boolean { if (!this.auth.isAuthenticated()) { this.router.navigate(["login"]); return false; } return true; } } 

Die AuthGuardService Implementierung verwendet den AuthGuardService beschriebenen AuthGuardService , um die Benutzerautorisierung zu überprüfen. Die canActivate Methode gibt einen booleschen Wert zurück, der unter der Bedingung der canActivate verwendet werden kann.


Jetzt können wir den erstellten Route Guard auf jede Route oder jeden Pfad anwenden. Zu diesem CanActivate geben wir beim Deklarieren von Routes im Abschnitt canActivate unseren Service an, der die CanActivate Schnittstelle erbt:


 export const ROUTES: Routes = [ { path: "", component: HomeComponent }, { path: "profile", component: UserComponent, canActivate: [AuthGuardService] }, { path: "**", redirectTo: "" } ]; 

In diesem Fall hat die /profile Route den optionalen Konfigurationswert canActivate . AuthGuard beschriebene AuthGuard wird als Argument an diese canActivate Eigenschaft übergeben. Als nächstes wird die canActivate Methode jedes Mal aufgerufen, wenn jemand versucht, auf den Pfad /profile zuzugreifen. Wenn der Benutzer autorisiert ist, erhält er Zugriff auf den Pfad /profile , andernfalls wird er auf den Pfad /login umgeleitet.


Sie sollten sich bewusst sein, dass Sie mit canActivate die Komponente auf diesem Pfad weiterhin aktivieren können, jedoch nicht zu ihr wechseln können. Wenn Sie die Aktivierung und das Laden der Komponente schützen müssen, können wir in diesem Fall canLoad . CanLoad Implementierung kann analog erfolgen.


Kochen RxJS


Angular basiert auf RxJS. RxJS ist eine Bibliothek zum Arbeiten mit asynchronen und ereignisbasierten Datenströmen unter Verwendung beobachtbarer Sequenzen. RxJS ist eine JavaScript-Implementierung der ReactiveX-API. Fehler, die beim Arbeiten mit dieser Bibliothek auftreten, sind größtenteils mit oberflächlichen Kenntnissen der Grundlagen ihrer Implementierung verbunden.


Verwenden Sie Async, anstatt sich für Ereignisse anzumelden


Eine große Anzahl von Entwicklern, die erst kürzlich das Angular-Framework verwendet haben, verwenden die subscribe von Observable , um Daten in der Komponente zu empfangen und zu speichern:


 @Component({ selector: "my-component", template: ` <span>{{localData.name}} : {{localData.value}}</span>` }) export class MyComponent { localData; constructor(http: HttpClient) { http.get("api/data").subscribe(data => { this.localData = data; }); } } 

Stattdessen können wir die Vorlage mithilfe der asynchronen Pipe abonnieren:


 @Component({ selector: "my-component", template: ` <p>{{data.name | async}} : {{data.value | async}}</p>` }) export class MyComponent { data; constructor(http: HttpClient) { this.data = http.get("api/data"); } } 

Durch das Abonnieren einer Vorlage vermeiden wir Speicherverluste, da Angular Observable automatisch abmeldet, wenn eine Komponente beschädigt wird. In diesem Fall bietet die Verwendung der asynchronen Pipe für HTTP-Anforderungen praktisch keine Vorteile, außer einer - async bricht die Anforderung ab, wenn die Daten nicht mehr benötigt werden, und schließt die Verarbeitung der Anforderung nicht ab.


Viele Funktionen von Observables beim manuellen Abonnieren nicht verwendet. Observables Verhalten von Observables kann durch Wiederholen (z. B. Wiederholen einer http-Anforderung), zeitgesteuertes Aktualisieren oder Vor-Caching erweitert werden.


Verwenden Sie $ , um Observable zu bezeichnen


Der nächste Absatz bezieht sich auf das Design der Anwendungsquellcodes und folgt aus dem vorherigen Absatz. Um Observable von einfachen Variablen zu unterscheiden, können Sie häufig den Rat hören, das Zeichen " $ " im Namen einer Variablen oder eines Felds zu verwenden. Dieser einfache Trick beseitigt Verwirrung bei Variablen bei Verwendung von Async.


 import { Component } from "@angular/core"; import { Observable } from "rxjs/Rx"; import { UserClient } from "../services/user.client"; import { User } from "../services/user"; @Component({ selector: "user-list", template: ` <ul class="user_list" *ngIf="(users$ | async).length"> <li class="user" *ngFor="let user of users$ | async"> {{ user.name }} - {{ user.birth_date }} </li> </ul>` }) export class UserList { public users$: Observable<User[]>; constructor(public userClient: UserClient) {} public ngOnInit() { this.users$ = this.client.getUsers(); } } 

Wann abbestellen (abbestellen)


Die häufigste Frage, die ein Entwickler hat, wenn er Angular kurz kennenlernt, ist, wann Sie sich noch abmelden müssen und wann nicht. Um diese Frage zu beantworten, müssen Sie zunächst entscheiden, welche Art von Observable derzeit verwendet wird. In Angular gibt es zwei Arten von Observable - endlich und unendlich, einige erzeugen eine endliche, andere jeweils eine unendliche Anzahl von Werten.


Http Observable ist kompakt und Listener / Listener von DOM-Ereignissen sind unendlich Observable .


Wenn das Abonnieren der Werte eines unendlichen Observable manuell erfolgt (ohne Verwendung einer asynchronen Pipe), muss unbedingt eine Antwort erfolgen. Wenn wir ein endliches Observable manuell abonnieren, ist es nicht erforderlich, sich abzumelden. RxJS kümmert sich darum. Bei kompakten Observables wir uns abmelden, wenn Observable eine längere Ausführungszeit als erforderlich hat, z. B. eine Mehrfach-HTTP-Anforderung.


Ein Beispiel für kompakte Observables :


 export class SomeComponent { constructor(private http: HttpClient) { } ngOnInit() { Observable.timer(1000).subscribe(...); this.http.get("http://api.com").subscribe(...); } } 

Beispiel für unendliche Observablen


 export class SomeComponent { constructor(private element : ElementRef) { } interval: Subscription; click: Subscription; ngOnInit() { this.interval = Observable.interval(1000).subscribe(...); this.click = Observable.fromEvent(this.element.nativeElement, "click").subscribe(...); } ngOnDestroy() { this.interval.unsubscribe(); this.click.unsubscribe(); } } 

Im Folgenden finden Sie detaillierter die Fälle, in denen Sie sich abmelden müssen


  1. Das Formular und die einzelnen Steuerelemente, die sie abonniert haben, müssen abbestellt werden:

 export class SomeComponent { ngOnInit() { this.form = new FormGroup({...}); this.valueChangesSubs = this.form.valueChanges.subscribe(...); this.statusChangesSubs = this.form.statusChanges.subscribe(...); } ngOnDestroy() { this.valueChangesSubs.unsubscribe(); this.statusChangesSubs.unsubscribe(); } } 

  1. Router Laut Dokumentation sollte sich Angular selbst abmelden, dies geschieht jedoch nicht . Um weitere Probleme zu vermeiden, schreiben wir daher selbst:

 export class SomeComponent { constructor(private route: ActivatedRoute, private router: Router) { } ngOnInit() { this.route.params.subscribe(..); this.route.queryParams.subscribe(...); this.route.fragment.subscribe(...); this.route.data.subscribe(...); this.route.url.subscribe(..); this.router.events.subscribe(...); } ngOnDestroy() { //        observables } } 

  1. Endlose Sequenzen. Beispiele sind Sequenzen, die mit interva() oder Ereignis-Listenern (fromEvent()) :

 export class SomeComponent { constructor(private element : ElementRef) { } interval: Subscription; click: Subscription; ngOnInit() { this.intervalSubs = Observable.interval(1000).subscribe(...); this.clickSubs = Observable.fromEvent(this.element.nativeElement, "click").subscribe(...); } ngOnDestroy() { this.intervalSubs.unsubscribe(); this.clickSubs.unsubscribe(); } } 

takeUntil und takeWhile


Um die Arbeit mit unendlichen Observables in RxJS zu vereinfachen, gibt es zwei praktische Funktionen: takeUntil und takeWhile . Sie führen dieselbe Aktion aus: Observable am Ende einer Bedingung vom Observable abmelden, besteht der Unterschied nur in den akzeptierten Werten. takeWhile akzeptiert einen boolean takeUntil und takeUntil einen Subject .
Beispiel: takeWhile :


 export class SomeComponent implements OnDestroy, OnInit { public user: User; private alive: boolean = true; public ngOnInit() { this.userService .authenticate(email, password) .takeWhile(() => this.alive) .subscribe(user => { this.user = user; }); } public ngOnDestroy() { this.alive = false; } } 

In diesem Fall wird das Observable abgemeldet, wenn das alive Flag geändert wird. In diesem Beispiel können Sie sich abmelden, wenn die Komponente zerstört wird.
takeUntil Beispiel:


 export class SomeComponent implements OnDestroy, OnInit { public user: User; private unsubscribe: Subject<void> = new Subject(void); public ngOnInit() { this.userService.authenticate(email, password) .takeUntil(this.unsubscribe) .subscribe(user => { this.user = user; }); } public ngOnDestroy() { this.unsubscribe.next(); this.unsubscribe.complete(); } } 

In diesem Fall melden wir zum Abbestellen von Observable dass der subject den nächsten Wert annimmt und ihn vervollständigt.


Durch die Verwendung dieser Funktionen werden Lecks vermieden und die Arbeit beim Abbestellen von Daten vereinfacht. Welche Funktion soll verwendet werden? Die Antwort auf diese Frage sollte sich an persönlichen Vorlieben und aktuellen Anforderungen orientieren.


Statusverwaltung in Angular-Anwendungen, @ ngrx / store


Sehr oft müssen wir bei der Entwicklung komplexer Anwendungen den Status speichern und auf Änderungen reagieren. Es gibt viele Bibliotheken für Anwendungen, die im ReactJs-Framework entwickelt wurden, mit denen Sie den Status der Anwendung steuern und auf ihre Änderungen reagieren können - Flux, Redux, Redux-Saga usw. Für Angular-Anwendungen gibt es einen RxJS-basierten Statuscontainer, der von Redux - @ ngrx / store inspiriert ist. Durch die ordnungsgemäße Verwaltung des Anwendungsstatus wird der Entwickler vor vielen Problemen bei der weiteren Erweiterung der Anwendung bewahrt.


Warum Redux?
Redux positioniert sich als vorhersehbarer Statuscontainer für JavaScript-Anwendungen. Redux ist von Flux und Elm inspiriert.


Redux schlägt vor, die Anwendung als einen Anfangszustand zu betrachten, der durch eine Abfolge von Aktionen geändert werden kann. Dies kann ein guter Ansatz zum Erstellen komplexer Webanwendungen sein.


Redux ist keinem bestimmten Framework zugeordnet, und obwohl es für React entwickelt wurde, kann es mit Angular oder jQuery verwendet werden.


Die Hauptpostulate von Redux:


  • ein Repository für den gesamten Status der Anwendung
  • schreibgeschützter Zustand
  • Änderungen werden durch "reine" Funktionen vorgenommen, die folgenden Anforderungen unterliegen:
  • darf keine externen Anrufe über ein Netzwerk oder eine Datenbank tätigen;
  • einen Wert zurückgeben, der nur von den übergebenen Parametern abhängt;
  • Argumente sind unveränderlich, d.h. Funktionen sollten sie nicht ändern;
  • Das Aufrufen einer reinen Funktion mit denselben Argumenten gibt immer dasselbe Ergebnis zurück.

Ein Beispiel für eine Zustandsverwaltungsfunktion:


 // counter.ts import { ActionReducer, Action } from "@ngrx/store"; export const INCREMENT = "INCREMENT"; export const DECREMENT = "DECREMENT"; export const RESET = "RESET"; export function counterReducer(state: number = 0, action: Action) { switch (action.type) { case INCREMENT: return state + 1; case DECREMENT: return state - 1; case RESET: return 0; default: return state; } } 

Der StoreModule.provideStore(reducers) wird in das Hauptmodul der Anwendung importiert und mithilfe der Funktion StoreModule.provideStore(reducers) für den StoreModule.provideStore(reducers) verfügbar gemacht:


 // app.module.ts import { NgModule } from "@angular/core"; import { StoreModule } from "@ngrx/store"; import { counterReducer } from "./counter"; @NgModule({ imports: [ BrowserModule, StoreModule.provideStore({ counter: counterReducer }) ] }) export class AppModule { } 

Als nächstes wird der Store Service in die erforderlichen Komponenten und Services eingeführt. Mit der Funktion store.select () wird der Status "Slice" ausgewählt:


 // app.component.ts ... interface AppState { counter: number; } @Component({ selector: "my-app", template: ` <button (click)="increment()">Increment</button> <div>Current Count: {{ counter | async }}</div> <button (click)="decrement()">Decrement</button> <button (click)="reset()">Reset Counter</button>` }) class AppComponent { counter: Observable<number>; constructor(private store: Store<AppState>) { this.counter = store.select("counter"); } increment() { this.store.dispatch({ type: INCREMENT }); } decrement() { this.store.dispatch({ type: DECREMENT }); } reset() { this.store.dispatch({ type: RESET }); } } 

@ ngrx / router-store


In einigen Fällen ist es zweckmäßig, den Status der Anwendung mit der aktuellen Route der Anwendung zu verknüpfen. In diesen Fällen ist das Modul @ ngrx / router-store vorhanden. Damit die Anwendung den router-store zum Speichern des Status verwenden kann, verbinden routerReducer einfach routerReducer und fügen Sie RouterStoreModule.connectRoute im Hauptmodul der Anwendung hinzu:


 import { StoreModule } from "@ngrx/store"; import { routerReducer, RouterStoreModule } from "@ngrx/router-store"; @NgModule({ imports: [ BrowserModule, StoreModule.provideStore({ router: routerReducer }), RouterStoreModule.connectRouter() ], bootstrap: [AppComponent] }) export class AppModule { } 

RouterState nun den RouterState zum Hauptstatus der Anwendung hinzu:


 import { RouterState } from "@ngrx/router-store"; export interface AppState { ... router: RouterState; }; 

Darüber hinaus können wir den Anfangszustand der Anwendung angeben, wenn wir store speichern:


 StoreModule.provideStore( { router: routerReducer }, { router: { path: window.location.pathname + window.location.search } } ); 

Unterstützte Aktionen:


 import { go, replace, search, show, back, forward } from "@ngrx/router-store"; //      store.dispatch(go(["/path", { routeParam: 1 }], { query: "string" })); //        store.dispatch(replace(["/path"], { query: "string" })); //        store.dispatch(show(["/path"], { query: "string" })); //       store.dispatch(search({ query: "string" })); //   store.dispatch(back()); //   store.dispatch(forward()); 

UPD: Der Kommentar schlug vor, dass diese Aktionen in der neuen Version @ngrx für die neue Version https://github.com/ngrx/platform/blob/master/MIGRATION.md#ngrxrouter-store nicht verfügbar sind


Durch die Verwendung des Statuscontainers werden viele Probleme bei der Entwicklung komplexer Anwendungen behoben. Es ist jedoch wichtig, die Zustandsverwaltung so einfach wie möglich zu gestalten. Sehr oft muss man sich mit Anwendungen befassen, bei denen es zu einer übermäßigen Verschachtelung von Zuständen kommt, was das Verständnis der Anwendung nur erschwert.


Code Organisation


Sperrige Ausdrücke beim import loswerden


Vielen Entwicklern ist eine Situation bekannt, in der die Ausdrücke beim import ziemlich umständlich sind. Dies macht sich insbesondere bei großen Anwendungen bemerkbar, bei denen es viele wiederverwendbare Bibliotheken gibt.


 import { SomeService } from "../../../core/subpackage1/subpackage2/some.service"; 

Was ist sonst noch schlecht in diesem Code? Falls Sie unsere Komponente in ein anderes Verzeichnis übertragen müssen, sind die Ausdrücke beim import nicht gültig.


In diesem Fall können wir durch die Verwendung von Aliasen beim import umfangreichen Ausdrücken Abstand nehmen und unseren Code viel sauberer machen. Um das Projekt für die Verwendung von Aliasen vorzubereiten, müssen Sie die Eigenschaften baseUrl und path in tsconfig.json :


 / tsconfig.json { "compilerOptions": { ... "baseUrl": "src", "paths": { "@app/*": ["app/*"], "@env/*": ["environments/*"] } } } 

Mit diesen Änderungen ist es einfach genug, Plug-Ins zu verwalten:


 import { Component, OnInit } from "@angular/core"; import { Observable } from "rxjs/Observable"; /*    */ import { SomeService } from "@app/core"; import { environment } from "@env/environment"; /*      */ import { LocalService } from "./local.service"; @Component({ /* ... */ }) export class ExampleComponent implements OnInit { constructor( private someService: SomeService, private localService: LocalService ) { } } 

In diesem Beispiel wird SomeService direkt aus @app/core anstelle eines umfangreichen Ausdrucks importiert (z. B. @app/core/some-package/some.service ). Dies ist dank des Reexports öffentlicher Komponenten in der Hauptdatei index.ts . Es ist ratsam, für jedes Paket, in dem Sie alle öffentlichen Module erneut exportieren müssen, eine index.ts Datei zu erstellen:


 // index.ts export * from "./core.module"; export * from "./auth/auth.service"; export * from "./user/user.service"; export * from "./some-service/some.service"; 

Core-, Shared- und Feature-Module


Für eine flexiblere Verwaltung von Anwendungskomponenten wird in der Literatur und in verschiedenen Internetressourcen häufig empfohlen, die Sichtbarkeit der Komponenten zu verbessern. In diesem Fall wird die Verwaltung der Komponenten der Anwendung vereinfacht. Die folgende Trennung wird am häufigsten verwendet: Core-, Shared- und Feature-Module.


Coremodule


Der Hauptzweck von CoreModule besteht darin, Dienste zu beschreiben, die eine Instanz für die gesamte Anwendung haben (d. H. Das Singleton-Muster implementieren). Dazu gehört häufig ein Autorisierungsdienst oder ein Dienst zum Abrufen von Benutzerinformationen. CoreModule Beispiel:


 import { NgModule, Optional, SkipSelf } from "@angular/core"; import { CommonModule } from "@angular/common"; import { HttpClientModule } from "@angular/common/http"; /*  */ import { SomeSingletonService } from "./some-singleton/some-singleton.service"; @NgModule({ imports: [CommonModule, HttpClientModule], declarations: [], providers: [SomeSingletonService] }) export class CoreModule { /*   CoreModule    NgModule the AppModule */ constructor( @Optional() @SkipSelf() parentModule: CoreModule ) { if (parentModule) { throw new Error("CoreModule is already loaded. Import only in AppModule"); } } } 

Sharedmodule


Dieses Modul beschreibt einfache Komponenten. Diese Komponenten importieren oder injizieren keine Abhängigkeiten von anderen Modulen in ihre Konstruktoren. Sie sollten alle Daten über die Attribute in der Komponentenvorlage erhalten. SharedModule ist nicht vom Rest unserer Anwendung abhängig. Es ist auch ein idealer Ort zum Importieren und erneuten Exportieren von Angular Material-Komponenten oder anderen UI-Bibliotheken.


 import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { FormsModule } from "@angular/forms"; import { MdButtonModule } from "@angular/material"; /*  */ import { SomeCustomComponent } from "./some-custom/some-custom.component"; @NgModule({ imports: [CommonModule, FormsModule, MdButtonModule], declarations: [SomeCustomComponent], exports: [ /*  Angular Material*/ CommonModule, FormsModule, MdButtonModule, /*   */ SomeCustomComponent ] }) export class SharedModule { } 

Featuremodule


Hier können Sie den Angular Style Guide wiederholen. Für jede unabhängige Anwendungsfunktion wird ein separates FeatureModule erstellt. FeatureModule sollte Dienste nur aus CoreModule . Wenn ein Modul zum Importieren eines Dienstes von einem anderen Modul benötigt wird, sollte dieser Dienst möglicherweise nach CoreModule .


In einigen Fällen muss der Dienst nur von einigen Modulen verwendet werden, und es ist nicht erforderlich, ihn nach CoreModule zu CoreModule . In diesem Fall können Sie ein spezielles SharedModule erstellen, das nur in diesen Modulen verwendet wird.
, — , - , , CoreModule , SharedModule .


, . , . , , .


Referenzliste


  1. https://github.com/ngrx/store
  2. http://stepansuvorov.com/blog/2017/06/angular-rxjs-unsubscribe-or-not-unsubscribe/
  3. https://medium.com/@tomastrajan/6-best-practices-pro-tips-for-angular-cli-better-developer-experience-7b328bc9db81
  4. https://habr.com/post/336280/
  5. https://angular.io/docs

Source: https://habr.com/ru/post/de425959/


All Articles