2017-09-24 3 views
0

Ich bin ziemlich neu in der Angular Szene und ich fange an, mir die Haare aus einem unerwarteten Verhalten zu ziehen.Angular - Observables in Diensten

Ich habe den folgenden Code in meinem IntegrationsService

queryList(): Observable<ApiList[]> { 
    if(this.loadingApiList == true) { 
     return Observable.of(this.queryListResult); 
    } 
    this.loadingApiList = true; 
    return this._http 
     .get('samples/apiList.json').map((response: Response) => <ApiList[]> response.json()) 
     .do(data => { 
     this.queryListResult = data; 
     }); 
} 

Wie ich diesen Anruf stoppen wird zweimal beim Laden der Seite von einer Route ausgeführt?

Eine Komponente diese Methode aufruft und erstellt eine Liste in der Navigation, ruft eine andere Komponente die gleiche Dienstleistung für die folgende Methode:

getApiName(IT_ID:number) { 
    console.log(this.queryListResult); 
    let obj = this.queryListResult.find(o => o.IT_ID == IT_ID); 
    if(obj) { 
     return obj.IT_Name; 
    } 
    return false; 
} 

Dies funktioniert perfekt, wenn ich nicht Routen bin mit aber wenn ich routes Der getApiName gibt ein undefiniertes Ergebnis zurück.

Ich habe das Problem-Objekt auch verwendet, und das hat das Problem gelöst, aber dann wurden zwei Aufrufe an die JSON-Datei gemacht, das ist, was ich verwendet habe.

resolve(route: ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<any>{ 
    return this._service.queryList() // We need this to finish first. 
    } 

Ich bin jetzt wirklich stecken - ich kann es bekommen arbeiten die Entschlossenheit verwenden, aber es ist verschwenderisch mit dem .json zwei Anrufe haben.

Was ist der richtige Weg, dies zu tun?

+0

Das Setzen des Flags bedeutet nicht unbedingt, dass die Operation abgeschlossen ist und das Ergebnis der Abfrageliste zugewiesen wurde. Eine Methode, um dies zu erreichen, besteht darin, die Daten über eine beobachtbare Subjekt-basierte Datenmenge auszusetzen und einmal einen Abruf auszulösen, siehe z. https://blog.jonrshar.pe/2017/Apr/09/async-angular-data.html wo ich zeige, wie wir dieses Muster benutzt haben. – jonrsharpe

Antwort

1

Wie kann ich diesen Aufruf zweimal beim Seitenladen von einer Route aus stoppen?

Dies passiert, weil Sie zweimal abonnieren. Observables sind faul und jedes Mal, wenn Sie eines mit .subscribe() aufrufen, wird es starten: in Ihrem Fall wird es eine Netzwerkanforderung auslösen. Denken Sie an .subscribe() in Observablen wie .call() oder einfach () für Funktionen: Jedes Mal, wenn Sie eine Funktion aufrufen, wird es ausgeführt.

In diesem Sinne wird das Verhalten, auf das Sie stoßen, erwartet. Es gibt verschiedene Möglichkeiten, dies zu lösen, die ähnlich sind, sich aber je nach Anwendungsfall geringfügig unterscheiden; dh. das genaue Timing beim An- und Abmelden. Im Allgemeinen wird das, was Sie suchen, multicasting genannt.

Die einfachste Form des Multicasting ist the .shared() operator. Aus der official docs; Aber notieren Sie die Betonung (meins):

Gibt ein neues Observable zurück, das das ursprüngliche Observable multicasts (teilt). Solange es mindestens einen Abonnenten gibt wird diese Observable abonniert und sendet Daten. Wenn sich alle Abonnenten abgemeldet haben, werden sie sich von der Quelle Observable abmelden. Da das Observable Multicasting ist, wird der Stream heiß. Dies ist ein Alias ​​für .publish().refCount().

Was Sie interessieren könnte, ist in der Erstellung eines BehvaiorSubject, wie es den Begriff "aktuellen Wert" hat. Dann wird Ihr API-Aufruf nichts zurückgeben, er löst lediglich die Anfrage aus.

Hier ist ein Proof-of-concept:

export class Service { 

    public data$ = new BehaviorSubject(null); 

    public get() { 
    this.http.get(`some/data`).subscribe(data => this.data$.next(data)) 
    } 

} 

null ist der Wert jeder Teilnehmer synchron, wenn sie data$ abonniert bekommen. Du kannst es so benutzen.

this.get() 
this.data$.subscribe(data => /* ... */) 

Natürlich können Sie diese in etwas schöneres, wickeln und beginnen sogar Dinge wie Cache-Implementierung usw.


Einige nützliche Links:

+0

Vielen Dank für die ausführliche Antwort. Würde die Verwendung des BehaviorSubject nicht das gleiche Problem mit zwei Anfragen verursachen, die von jeder Komponente verursacht werden, die get() aufruft? – Colin

+0

Stimmt, deshalb habe ich gesagt, das Setup, das ich hier gegeben habe, ist nur ein Ausgangspunkt, um das 'BehaviorSubject' zu verstehen. Die Idee ist, dass Sie behandeln sollten, wie oft dieser Endpunkt mit benutzerdefinierter Logik aufgerufen werden kann. Sie möchten _never_ nicht erneut aufrufen, da Daten veralten könnten. Aus Sicht von RxJS können die beiden Aufrufe, die Sie ausführen, möglicherweise unterschiedliche Ergebnisse liefern. Es liegt an Ihnen, die Antwort irgendwie zu cachen und festzustellen, dass die Antwort gültig ist. –

+0

Nur um klar zu sein, wenn ich die get() auf der ngOnInit des Dienstes aufgerufen, um sicherzustellen, dass die Daten vorhanden sind. Alle abonnierten Komponenten vor und nach diesem get() erhalten die aktualisierte Kopie der Daten, wenn sie mit dem Abonnement verfügbar ist? – Colin

Verwandte Themen