5 Dinge, die ich gerne wissen würde, als ich mit Angular anfing

Modern Angular ist ein leistungsstarkes Framework mit vielen Funktionen, mit denen auf den ersten Blick komplexe Konzepte und Mechanismen einhergehen. Dies macht sich insbesondere bei jenen bemerkbar, die gerade erst im Frontend und insbesondere bei Angular angefangen haben, zu arbeiten.


Das gleiche Problem hatte ich auch, als ich vor ungefähr zwei Jahren zu Tinkoff kam und die Position eines Junior Frontend-Entwicklers innehatte und in die Welt von Angular eintauchte. Deshalb biete ich Ihnen eine kurze Geschichte über fünf Dinge an, deren Verständnis meine Arbeit zunächst sehr erleichtern würde.



Abhängigkeitsinjektion (DI)


Zuerst ging ich in die Komponente und sah, dass der Klassenkonstruktor einige Argumente enthielt. Ich habe die Arbeit der Klassenmethoden ein wenig analysiert, und es wurde klar, dass dies einige externe Abhängigkeiten sind. Aber wie sind sie in den Unterricht gekommen? Wo hieß der Konstruktor?


Ich schlage sofort vor, ein Beispiel zu verstehen, aber dafür brauchen wir eine Klasse. Wenn in "normalem" JavaScript OOP mit bestimmten "Hacks" vorhanden ist, gibt es zusammen mit ES6 eine "echte" Syntax. Angular verwendet TypeScript von Anfang an, wobei die Syntax in etwa gleich ist. Daher schlage ich vor, es weiter zu verwenden.


Stellen Sie sich vor, es gibt eine JokerService Klasse in unserer Anwendung, die Witze verwaltet. Die Methode getJokes() gibt eine Liste von Witzen zurück. Angenommen, wir verwenden es an drei Stellen. Wie bekomme ich Witze an drei verschiedenen Stellen im Code? Es gibt verschiedene Möglichkeiten:


  1. Erstellen Sie an jeder Stelle eine Instanz der Klasse. Aber warum müssen wir den Speicher verstopfen und so viele identische Dienste erstellen? Und wenn es 100 Plätze gibt?
  2. Machen Sie die Methode statisch und rufen Sie Daten mit JokerService.getJokes () ab.
  3. Implementieren Sie eines der Entwurfsmuster. Wenn wir den Dienst für die gesamte Anwendung benötigen, ist dies Singleton. Dafür müssen Sie jedoch eine neue Logik in die Klasse schreiben.

Wir haben also drei recht funktionierende Optionen. Das erste wird uns nicht passen - in diesem Fall ist es unwirksam. Wir möchten keine zusätzlichen Kopien erstellen, da diese vollkommen identisch sind. Es bleiben zwei Möglichkeiten.


Lassen Sie uns die Aufgabe komplizieren, um zu verstehen, welche Methode am besten zu uns passt. Angenommen, an dritter Stelle müssen wir aus irgendeinem Grund unseren eigenen Service mit bestimmten Parametern erstellen. Dies kann ein bestimmter Autor sein, die Länge des Witzes, die Sprache und mehr. Was machen wir dann?


Bei der statischen Methode müssen Sie die Einstellungen bei jedem Aufruf übergeben, da die Klasse allen Orten gemeinsam ist. Das heißt, bei jedem Aufruf von getJokes() werden alle für diesen Ort eindeutigen Parameter übergeben. Natürlich ist es besser, sie beim Instanziieren zu übergeben und dann einfach die getJokes() -Methode getJokes() .


Es stellt sich heraus, dass uns auch die zweite Option nicht zusagt: Wir duplizieren immer viel Code an jedem Ort. Es bleibt nur Singleton, der wiederum die Logik aktualisieren muss, jedoch mit Variationen. Aber wie kann man verstehen, welche Option wir brauchen?


Wenn Sie dachten, dass Sie einfach ein Objekt erstellen und den Schlüssel verwenden können, um den gewünschten Dienst in Anspruch zu nehmen, kann ich Ihnen gratulieren: Sie haben gerade festgestellt, wie die Abhängigkeitsinjektion im Allgemeinen funktioniert. Aber gehen wir etwas tiefer.


Stellen Sie sich vor, dass JokerService zwei weitere Dienste benötigt, von denen einer optional ist und der zweite an einer bestimmten Stelle ein spezielles Ergebnis liefern sollte, um sicherzustellen, dass ein Mechanismus erforderlich ist, um die richtigen Instanzen zu ermitteln. Es ist nicht schwer.


Abhängigkeitsinjektion in Angular


Wie in der Dokumentation angegeben , ist DI ein wichtiges Entwurfsmuster für eine Anwendung. Angular verfügt über ein eigenes Abhängigkeits-Framework, das in Angular verwendet wird, um die Effizienz und Modularität zu erhöhen.


Im Allgemeinen ist die Abhängigkeitsinjektion ein leistungsfähiger Mechanismus, mit dem eine Klasse die erforderlichen Abhängigkeiten von außerhalb erhält, anstatt Instanzen selbst zu erstellen.


Lassen Sie sich von der Syntax und den Dateien mit der html Erweiterung nicht verwirren. Jede Komponente in Angular ist ein reguläres JavaScript-Objekt, eine Instanz einer Klasse. Allgemein ausgedrückt: Wenn Sie eine Komponente in eine Vorlage einfügen, wird eine Instanz der Komponentenklasse erstellt. Dementsprechend können Sie zu diesem Zeitpunkt die erforderlichen Abhängigkeiten an den Konstruktor übergeben. Betrachten Sie nun ein Beispiel:


 @Component({ selector: 'jokes', template: './jokes.template.html', }) export class JokesComponent { private jokes: Observable<IJoke[]>; constructor(private jokerService: JokerService) { this.jokes = this.jokerService.getJokes(); } } 

Im Komponentenkonstruktor geben wir einfach an, dass wir einen JokerService benötigen. Wir schaffen es nicht selbst. Wenn es fünf weitere Komponenten gibt, die es verwenden, verweisen sie alle auf dieselbe Instanz. All dies ermöglicht es uns, Zeit zu sparen, Kesselplatten zu eliminieren und sehr produktive Anwendungen zu schreiben.


Anbieter


Und jetzt schlage ich vor, mich mit dem Fall zu befassen, wenn Sie verschiedene Instanzen des Dienstes benötigen. Schauen Sie sich zunächst den Service selbst an:


 @Injectable({ providedIn: 'root', //   ,   «»  }) export class JokerService { getJokes(): Observable<IJoke[]> { //     } } 

Wenn es sich bei dem Dienst um einen Dienst für die gesamte Anwendung handelt, ist diese Option ausreichend. Aber was ist, wenn wir beispielsweise zwei Implementierungen von JokerService ? Oder ist es nur aus irgendeinem Grund so, dass eine bestimmte Komponente eine eigene Service-Instanz benötigt? Die Antwort ist einfach: provider .


Der Einfachheit halber werde ich den provider Anbieter nennen , und der Vorgang des Ersetzens eines Werts in eine Klasse wird überprüft . So können wir auf unterschiedliche Weise und an unterschiedlichen Orten Serviceleistungen erbringen. Beginnen wir mit dem letzten. Es stehen drei Optionen zur Verfügung:


  • provideIn: 'root' die gesamte Anwendung im Service Decorator selbst provideIn: 'root' an.
  • Im Modul - provideIn: JokesModule den Provider im Service Decorator als provideIn: JokesModule oder im Decorator des @NgModule providers: [JokerService] Moduls @NgModule providers: [JokerService] .
  • In der Komponente - Geben Sie den Anbieter im Dekorator der Komponente wie im Modul an.

Der Ort wird je nach Ihren Bedürfnissen gewählt. Wir haben den Ort herausgefunden, gehen wir zum Mechanismus selbst über. Wenn wir im Service einfach provideIn: root angeben, entspricht dies dem folgenden Eintrag im Modul:


 @NgModule({ // ...     providers: [{provide: JokerService, useClass: JokerService}], }) //   

Dies kann in etwa so lauten: "Wenn ein JokerService angefordert wird, geben Sie eine Instanz der JokerService» Klasse an JokerService» Von hier aus können Sie eine bestimmte Instanz auf verschiedene Arten abrufen:


  • Per Token - Sie müssen ein InjectionToken angeben und einen Dienst darauf erhalten. Beachten Sie, dass Sie in den folgenden Beispielen in " provide dasselbe Token übergeben können:


     const JOKER_SERVICE_TOKEN = new InjectionToken<string>('JokerService'); // ...     [{provide: JOKER_SERVICE_TOKEN, useClass: JokerService}]; 

  • Nach Klasse - Sie können die Klasse ersetzen. Zum Beispiel werden wir nach JokerService fragen und geben - JokerHappyService :


     [{provide: JokerService, useClass: JokerHappyService}]; 

  • Nach Wert - Sie können sofort die gewünschte Instanz zurückgeben:


     [{provide: JokerService, useValue: jokerService}]; 

  • Nach Factory - Sie können die Klasse durch eine Factory ersetzen, die beim Zugriff auf die gewünschte Instanz erstellt:


     [{provide: JokerService, useFactory: jokerServiceFactory}]; 


Das ist alles. Das heißt, um das Beispiel mit einer speziellen Instanz zu lösen, können Sie eine der oben genannten Methoden verwenden. Wählen Sie die für Ihre Bedürfnisse am besten geeignete aus.


Übrigens funktioniert DI nicht nur für Services, sondern generell für alle Entitäten, die Sie im Komponentenkonstruktor erhalten. Dies ist ein sehr leistungsfähiger Mechanismus, der voll ausgeschöpft werden sollte.


Eine kleine Zusammenfassung


Für ein vollständiges Verständnis schlage ich vor, den vereinfachten Mechanismus der Abhängigkeitsinjektion in Angular schrittweise anhand des Dienstbeispiels zu betrachten:


  1. Beim Initialisieren der Anwendung verfügt der Dienst über ein Token. Wenn wir es im Provider nicht speziell angegeben haben, dann ist dies JokerService.
  2. Wenn ein Dienst in einer Komponente angefordert wird, prüft der DI-Mechanismus, ob das übertragene Token vorhanden ist.
  3. Wenn das Token nicht existiert, gibt DI einen Fehler aus. In unserem Fall existiert das Token und der JokerService befindet sich darauf.
  4. Beim Erstellen der Komponente wird eine Instanz von JokerService als Argument an den Konstruktor übergeben.

Erkennung ändern


Als Argument für die Verwendung von Frameworks hören wir oft etwas wie „Das Framework erledigt alles für Sie - schneller und effizienter. Sie müssen an nichts denken. Verwalten Sie einfach die Daten. “ Vielleicht ist dies bei einer sehr einfachen Anwendung der Fall. Wenn Sie jedoch mit Benutzereingaben arbeiten und ständig mit Daten arbeiten müssen, müssen Sie nur wissen, wie der Prozess zum Erkennen von Änderungen und zum Rendern funktioniert.


In Angular ist die Änderungserkennung für die Überprüfung von Änderungen verantwortlich. Infolge verschiedener Vorgänge - Ändern des Werts einer Klasseneigenschaft, Abschließen eines asynchronen Vorgangs, Beantworten einer HTTP-Anforderung usw. - wird der Überprüfungsprozess in der gesamten Komponentenstruktur gestartet.


Da das Hauptziel des Prozesses darin besteht, zu verstehen, wie eine Komponente erneut gerendert wird, besteht das Wesentliche darin, die in den Vorlagen verwendeten Daten zu überprüfen. Wenn sie unterschiedlich sind, wird die Vorlage als "geändert" markiert und neu gezeichnet.


Zone.js


Es ist ziemlich einfach zu verstehen, wie Angular Klasseneigenschaften und synchrone Operationen verfolgt. Aber wie verfolgt es asynchron? Verantwortlich dafür ist die von einem der Angular-Entwickler erstellte Zone.js-Bibliothek.


Hier ist was es ist. Eine Zone an sich ist ein „Ausführungskontext“, kurz gesagt der Ort und Zustand, in dem der Code ausgeführt wird. Nachdem der asynchrone Vorgang abgeschlossen ist, wird die Rückruffunktion in derselben Zone ausgeführt, in der sie registriert wurde. So findet Angular heraus, wo die Änderung stattgefunden hat und was zu überprüfen ist.


Zone.js ersetzt mit seinen Implementierungen fast alle nativen asynchronen Funktionen und Methoden. Daher kann verfolgt werden, wann der callback einer asynchronen Funktion aufgerufen wird. Das heißt, Zone teilt Angular mit, wann und wo der Änderungsüberprüfungsprozess gestartet werden soll.


Erkennungsstrategien ändern


Wir haben herausgefunden, wie Angular eine Komponente überwacht und Änderungsprüfungen durchführt. Stellen Sie sich jetzt vor, Sie haben eine riesige Anwendung mit Dutzenden von Komponenten. Und für jeden Klick, jede asynchrone Operation, jede erfolgreich ausgeführte Anforderung wird eine Überprüfung über den gesamten Komponentenbaum gestartet. Höchstwahrscheinlich weist eine solche Anwendung schwerwiegende Leistungsprobleme auf.


Angular-Entwickler haben darüber nachgedacht und uns die Möglichkeit gegeben, eine Änderungserkennungsstrategie zu entwickeln, deren richtige Auswahl die Produktivität erheblich steigern kann.


Es gibt zwei Möglichkeiten zur Auswahl:


  • Standard - wie der Name schon sagt, ist dies die Standardstrategie, wenn für jede Aktion eine CD gestartet wird.
  • OnPush ist eine Strategie, bei der eine CD nur in wenigen Fällen gestartet wird:
    • wenn sich der Wert von @Input() geändert hat;
    • wenn ein Ereignis innerhalb der Komponente oder ihrer Nachkommen aufgetreten ist;
    • ob die Prüfung manuell gestartet wurde;
    • wenn ein neues Ereignis in Async Pipe eintrifft.

Aufgrund meiner eigenen Entwicklungserfahrung in Angular sowie der Erfahrung meiner Kollegen kann ich mit Sicherheit sagen, dass es besser ist, immer die OnPush Strategie OnPush , es sei denn, default wirklich erforderlich. Dies bietet Ihnen mehrere Vorteile:


  • Ein klares Verständnis der Funktionsweise des CD-Prozesses.
  • @Input() Arbeiten mit @Input() -Eigenschaften.
  • Leistungsgewinn.

Mit @Input()


Wie andere gängige Frameworks verwendet Angular einen Downstream-Datenstrom. Die Komponente akzeptiert Eingabeparameter, die mit dem @Input() . Betrachten Sie ein Beispiel:


 interface IJoke { author: string; text: string; } @Component({ selector: 'joke', template: './joke.template.html', }) export class JokeComponent { @Input() joke: IJoke; } 

Angenommen, es gibt eine oben beschriebene Komponente, die den Text des Witzes und des Autors anzeigt. Das Problem bei diesem Schreiben ist, dass Sie das übertragene Objekt versehentlich oder spezifisch mutieren können. Zum Beispiel Text oder Autor überschreiben.


 setAuthorNameOnly() { const name = this.joke.author.split(' ')[0]; this.joke.author = name; } 

Ich stelle sofort fest, dass dies ein schlechtes Beispiel ist, aber es zeigt deutlich, was passieren könnte. Um sich vor solchen Fehlern zu schützen, müssen Sie die Eingabeparameter schreibgeschützt machen. Dank dessen haben Sie ein Verständnis dafür, wie Sie richtig mit Daten arbeiten und eine CD erstellen. Auf dieser Basis sieht der beste Weg, eine Klasse zu schreiben, so aus:


 @Component({ selector: 'joke', template: './joke.template.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class JokeComponent { @Input() readonly joke: IJoke; @Output() updateName = new EventEmitter<string>(); setAuthorNameOnly() { const name = this.joke.author.split(' ')[0]; this.updateName.emit(name); } } 

Der beschriebene Ansatz ist keine Regel, sondern nur eine Empfehlung. Es gibt viele Situationen, in denen dieser Ansatz unpraktisch und ineffektiv ist. Mit der Zeit werden Sie verstehen, in welchem ​​Fall Sie die vorgeschlagene Methode zum Arbeiten mit Eingaben ablehnen können.


Rxjs


Natürlich könnte ich mich irren, aber es scheint, dass ReactiveX und reaktive Programmierung im Allgemeinen ein neuer Trend sind. Angular ist diesem Trend erlegen (oder hat ihn vielleicht erstellt) und verwendet standardmäßig RxJS. Die Grundlogik des gesamten Frameworks basiert auf dieser Bibliothek, daher ist es sehr wichtig, die Prinzipien der reaktiven Programmierung zu verstehen.


Aber was ist RxJS? Es kombiniert drei Ideen, die ich in einer ziemlich einfachen Sprache mit einigen Auslassungen enthüllen werde:


  • Das "Beobachter" -Muster ist eine Entität, die Ereignisse erzeugt, und es gibt einen Listener, der Informationen über diese Ereignisse erhält.
  • Das Iterator-Muster - ermöglicht Ihnen den sequentiellen Zugriff auf die Elemente eines Objekts, ohne dessen interne Struktur preiszugeben.
  • Funktionale Programmierung mit Sammlungen ist ein Muster, bei dem Logik in kleine und sehr einfache Komponenten zerlegt wird, von denen jede nur ein Problem löst.

Durch die Kombination dieser Muster können wir sehr einfach auf den ersten Blick komplexe Algorithmen beschreiben, zum Beispiel:


 private loadUnreadJokes() { this.showLoader(); //   fromEvent(document, 'load') .pipe( switchMap( () => this.http .get('/api/v1/jokes') //   .pipe(map((jokes: any[]) => jokes.filter(joke => joke.unread))), //   ), ) .subscribe( (jokes: any[]) => (this.jokes = jokes), //   error => { /*   */ }, () => this.hideLoader(), //       ); } 

Nur 18 Zeilen mit all den schönen Einkerbungen. Versuchen Sie nun, dieses Beispiel in Vanilla oder zumindest in jQuery umzuschreiben. Fast 100% davon beanspruchen mindestens doppelt so viel Platz und sind weniger aussagekräftig. Hier können Sie einfach die Linie mit Ihren Augen verfolgen und den Code wie ein Buch lesen.


Beobachtbar


Zu verstehen, dass Daten als Stream dargestellt werden können, kommt nicht sofort. Daher schlage ich vor, zu einer einfachen Analogie überzugehen. Stellen Sie sich vor, ein Stream ist ein Array von Daten, die nach Zeit sortiert sind. Zum Beispiel in dieser Ausführungsform:


 const observable = []; let counter = 0; const intervalId = setInterval(() => { observable.push(counter++); }, 1000); setTimeout(() => { clearInterval(intervalId); }, 6000); 

Wir betrachten den letzten Wert im Array als relevant. Jede Sekunde wird eine Zahl zum Array hinzugefügt. Wie können wir an anderer Stelle in der Anwendung feststellen, dass dem Array ein Element hinzugefügt wurde? In einer normalen Situation würden wir eine Art callback aufrufen und den Wert des Arrays darauf aktualisieren und dann einfach das letzte Element nehmen.


Dank der reaktiven Programmierung müssen Sie nicht nur viel neue Logik schreiben, sondern auch über die Aktualisierung von Informationen nachdenken. Dies kann mit einem einfachen Listener verglichen werden:


 document.addEventListener('click', event => {}); 

Sie können eine Menge EventListener in die gesamte Anwendung EventListener , und sie werden funktionieren, es sei denn, Sie kümmern sich absichtlich um das Gegenteil.


Reaktive Programmierung funktioniert auch. An einer Stelle erstellen wir einfach einen Datenstrom und löschen dort regelmäßig neue Werte. An einer anderen Stelle abonnieren wir diesen Strom und hören uns diese Werte einfach an. Das heißt, wir erfahren immer etwas über das Update und können damit umgehen.


Schauen wir uns nun ein reales Beispiel an:


 export class JokesListComponent implements OnInit { jokes$: Observable<IJoke>; authors$ = new Subject<string[]>(); unread$ = new Subject<number>(); constructor(private jokerService: JokerService) {} ngOnInit() { //  ,    subscribe()    this.jokes$ = this.jokerService.getJokes(); this.jokes$.subscribe(jokes => { this.authors$.next(jokes.map(joke => joke.author)); this.unread$.next(jokes.filter(joke => joke.unread).length); }); } } 

Dank dieser Logik aktualisieren wir beim Ändern von Daten in jokes automatisch die Daten zur Anzahl der ungelesenen Witze und zur Liste der Autoren. Wenn Sie mehrere Komponenten haben, von denen eine Statistiken über die Anzahl der von einem Autor gelesenen Witze sammelt und die zweite die durchschnittliche Länge der Witze berechnet, werden die Vorteile offensichtlich.


Prüfstand


Früher oder später versteht der Entwickler, dass Sie Tests schreiben müssen, wenn das Projekt nicht MVP ist. Und je mehr Tests geschrieben werden, desto klarer und detaillierter ist ihre Beschreibung, desto einfacher, schneller und zuverlässiger ist es, Änderungen vorzunehmen und neue Funktionen zu implementieren.


Angular hat dies wahrscheinlich vorausgesehen und uns ein leistungsfähiges Testwerkzeug zur Verfügung gestellt. Viele Entwickler versuchen zunächst, eine Technologie „von Anfang an“ zu beherrschen, ohne auf die Dokumentation einzugehen. Ich habe das gleiche getan, weshalb ich ziemlich spät alle verfügbaren Testmöglichkeiten "out of the box" erkannte.


Sie können alles in Angular testen. Wenn Sie jedoch nur Methoden instanziieren und aufrufen müssen, um eine reguläre Klasse oder einen regulären Dienst zu testen, ist die Situation mit der Komponente völlig anders.


Wie wir bereits herausgefunden haben, werden Abhängigkeiten dank DI außerhalb der Komponente genommen. Einerseits kompliziert dies das gesamte System ein wenig, andererseits gibt es uns große Möglichkeiten, Tests einzurichten und viele Fälle zu überprüfen. Ich schlage vor, das Beispiel einer Komponente zu verstehen:


 @Component({ selector: 'app-joker', template: '<some-dependency></some-dependency>', styleUrls: ['./joker.component.less'], }) export class JokerComponent { constructor( private jokesService: JokesService, @Inject(PARTY_TOKEN) private partyService: PartyService, @Optional() private sleepService: SleepService, ) {} makeNewFriend(): IFriend { if (this.sleepService && this.sleepService.isSleeping) { this.sleepService.wakeUp(); } const joke = this.jokesService.generateNewJoke(); this.partyService.goToParty('Pacha'); this.partyService.toSay(joke.text); const laughingPeople = this.partyService.getPeopleByReaction('laughing'); const girl = laughingPeople.find(human => human.sex === 'female'); const friend = this.partyService.makeFriend(girl); return friend; } } 

Im aktuellen Beispiel gibt es also drei Dienste. Einer wird auf die übliche Weise importiert, einer per Token und ein weiterer Dienst ist optional. Wie konfigurieren wir das Testmodul? Ich zeige sofort die fertige Ansicht:


 beforeEach(async(() => { TestBed.configureTestingModule({ imports: [SomeDependencyModule], declarations: [JokerComponent], //  ,    providers: [{provide: PARTY_TOKEN, useClass: PartyService}], }).compileComponents(); fixture = TestBed.createComponent(JokerComponent); component = fixture.componentInstance; fixture.detectChanges(); //    ,     })); 

TestBed können wir eine vollständige Simulation des erforderlichen Moduls durchführen. Sie können beliebige Dienste einbinden, Module ersetzen, Instanzen von Klassen von einer Komponente abrufen und vieles mehr. Nachdem wir das Modul bereits konfiguriert haben, gehen wir zu den Möglichkeiten über.


Unnötige Abhängigkeiten können vermieden werden


Eine Angular-Anwendung besteht aus Modulen, die andere Module, Dienste, Anweisungen und mehr enthalten können. Im Test müssen wir tatsächlich die Funktionsweise des Moduls wiederherstellen. Wenn wir in unserem Beispiel <some-dependency></some-dependency> in der Vorlage verwenden, bedeutet dies, dass wir auch SomeDependencyModule in den Test importieren müssen. Und wenn da Sucht ist? Sie müssen also auch importiert werden.
Wenn die Anwendung komplex ist, gibt es viele solche Abhängigkeiten. Das Importieren aller Abhängigkeiten führt dazu, dass in jedem Test die gesamte Anwendung lokalisiert und alle Methoden aufgerufen werden. Vielleicht passt uns das nicht.


Es gibt mindestens eine Möglichkeit, die notwendigen Abhängigkeiten zu beseitigen - schreiben Sie einfach die Vorlage neu. Angenommen, Sie haben Screenshot- oder Integrationstests und müssen das Erscheinungsbild der Komponente nicht testen. Dann reicht es aus, einfach die Funktionsweise der Methoden zu überprüfen. In diesem Fall können Sie die Konfiguration wie folgt schreiben:


 TestBed.configureTestingModule({ declarations: [JokerComponent], providers: [{provide: PARTY_TOKEN, useClass: PartyService}], }) .overrideTemplate(JokerComponent, '') //   ,   .compileComponents(); 

, . , . , . , , , , . — .



Injection Token , . . , , .


ts-mockito , , . Angular « ».


 //    export class MockPartyService extends PartyService { meetFriend(): IFriend { return {} as IFriend; } goToParty() {} toSay(some: string) { console.log(some); } } // ... TestBed.configureTestingModule({ declarations: [JokerComponent, MockComponent], providers: [{provide: PARTY_TOKEN, useClass: MockPartyService}], //    }).compileComponents(); 

. .



. , — , — . , :


  • .
  • — , . — .

— . , . — .


Zusammenfassung


Angular, . , , «».


, Angular - . HTTP-, , lazy-loading . Angular .

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


All Articles