2017-05-04 5 views
0

Ich habe eine Angular Http get Anfrage, die die Route zu einer der drei Routen abhängig von den zurückgegebenen Daten navigieren wird. Beispielcode unten:Angular2 Routing und Cancelling Observables Mid Stream

SomeComponent:

constructor(
    private someService: SomeService, 
    private router: Router 
) { } 

ngOnInit() { 
    this.someService.getSomeData().subscribe(
    () => { 
     console.log("getSomeData() did not route to a new link! Routing to a 3rd link"); 
     this.router.navigate(['home']); 
     }, 
    () => { 
     console.log("Some error occurred."); 
     }); 

Someservice:

constructor(
    private router: Router, 
    private someOtherService: SomeOtherService 
) { } 

getSomeData() { 
    return this.someOtherService.getSomeOtherData().map(data => { 
     if (data === 'someValue') { 
     console.log("The data was 'someValue'"); 
     this.router.navigate(['route1']); 
     } 
     else if (data == 'someOtherValue') { 
     console.log("The data was 'someOtherValue'"); 
     this.router.navigate(['route2']); 
     } 
    }); 
} 

SomeOtherService:

constructor(
    private http: Http 
) { } 

getSomeOtherData() { 
    return this.http.get(this.getDataUrl) { 
     .map((res: Response) => { 
     console.log("Sending over the data back to SomeService"); 
     let body = res.json(); 
     return body.data; 
     }) 
     .catch((err: Response) => { 
     console.log(err); 
     return Observable.throw(err); 
     }); 
    } 
} 

Das erwartete Verhalten ist, dass die Daten von SomeOtherService nach dem Empfang, der Router Navigieren Sie entweder zu route1 oder route2 abhängig von den Daten (die ich dachte irgendwo gelesen zu haben würde den Observable Stream stoppen? Vielleicht habe ich falsch gelesen). Wenn die Daten nicht übereinstimmen, fährt der Observable-Stream mit SomeComponent fort, der dann zu home navigiert.

Das tatsächliche Verhalten ist, dass der Router zunächst zu route1 oder route2 routet, aber da der Strom fortgesetzt wird, routet der Router schließlich zu home.

Also meine Frage ist, wenn Sie eine Rxjs Observable abonnieren, gibt es eine Möglichkeit, den Observable Mid-Stream abzubrechen/abmelden? Ich dachte, dass das Navigieren zu einer anderen Route das Observable abbrechen würde, aber das schien für mich nicht zu funktionieren.

Ich habe versucht, innerhalb der map Methode mit interessanten (ly bad) Ergebnisse abzubestellen. Ich habe auch versucht, andere Observable-Methoden zu betrachten, aber da ich neu bei rxjs bin, war ich mir nicht sicher, welche Methode am besten geeignet wäre (ich bin wirklich nur mit , , und (sort of) switchMap an dieser Stelle vertraut).

+0

Wenn Ihre einzige Observable durch das 'this.http.get' erzeugt wird, wird diese Observable automatisch beendet, wenn die Antwort zurückgegeben wird. Keine Notwendigkeit, es abzubrechen. – DeborahK

Antwort

2

Das erwartete Verhalten ist, dass nach Empfangen der Daten Von SomeOtherService navigiert der Router entweder zu route1 oder route2, abhängig von den Daten (die ich gelesen habe irgendwo würde der beobachtbare Strom stoppen? Vielleicht habe ich falsch gelesen). Wenn die Daten nicht übereinstimmen, fährt der Observable-Stream mit SomeComponent fort, der dann nach Hause navigiert.

Nicht ganz :-)

in einer anderen Ansicht Navigation vervollständigt alle Observables von den alten ActivatedRoute zur Verfügung gestellt. Die Observables, die Sie erstellt haben, werden nicht von einer ActivatedRoute erhalten (noch auf eine andere Weise an eine ActivatedRoute gebunden) und existieren daher unabhängig davon, welche Route aktiv ist.

Außerdem ist router.navigate(...) ein asynchroner Vorgang, dh es plant nur eine Navigation, wartet aber nicht auf die Navigation. Das heißt, selbst wenn Ihre Observables an die ActivatedRoute gebunden wäre, wären sie noch am Leben und ihre Beobachter benachrichtigt.

Sie können dieses Problem beheben, indem:

constructor(
    private router: Router, 
    private someOtherService: SomeOtherService 
) { } 

getSomeData() { 
    return this.someOtherService.getSomeOtherData().map(data => { 
     if (data === 'someValue') { 
     console.log("The data was 'someValue'"); 
     return ['route1']; 
     } 
     else if (data == 'someOtherValue') { 
     console.log("The data was 'someOtherValue'"); 
     return ['route2']; 
     } else { 
     return null; 
     } 
    }); 
} 

und verwenden Sie es mögen:

this.someService.getSomeData().subscribe({ 
    next: (route) => { 
    this.router.navigate(route || ['home']); 
    }, 
    error: (err) => { 
    console.log("Some error occurred.", err); 
    } 
}); 

BTW, um besser zu verstehen, was ein Observable ist, können Sie die ReactiveX documentation lesen möchten.

+0

Das löst das, was ich erreichen möchte. Aber nur um klar zu sein, ist eine gute Zusammenfassung, dass Observables nur Werte aussenden können und nur der Observer Observables abonnieren/abmelden kann? Daher kann ein Observable nicht einfach aufhören, Werte stromabwärts zu emittieren, was den Lebenszyklus des Beobachters/Observable beendet. – yoonjesung

+0

Ein Observable kann 'next',' completed' oder 'error' Ereignisse senden. 'completed' und' error' sind terminale Ereignisse. Ja, ein "Observable" kann es ablehnen, weitere Werte an seine "Beobachter" zu senden. Ihr Code hat dies nicht getan, weil Sie den Map-Operator verwendet haben, der den Rückgabewert der Mapping-Funktion ausgibt (in Ihrem Fall "undefined"). Sie können stattdessen einen 'flatMap'-Operator verwenden oder die Benachrichtigung per Hand durchführen, wie in Jagers Antwort, aber ich finde meine Lösung leichter zu verstehen. – meriton

1

Es gibt mehrere Möglichkeiten, von einer beobachtbaren

Die einfachste Art und Weise austragen ist ein Griff für das Abonnement zu halten und zum Beispiel in der OnDestroy Methode abmelden.

import { Subscription } from 'rxjs/Rx' 

... 

private sub: Subscription; 

constructor(
    private someService: SomeService, 
    private router: Router 
) { } 

ngOnInit() { 
    this.sub = this.someService.getSomeData().subscribe(..) 
} 

ngOnDestroy() { 
    if (this.sub) this.sub.unsubscribe(); 
} 

Auch die Navigation in der map() -Methode ist ein bisschen seltsam. Der Operator map() wird verwendet, um einen beobachtbaren Stream in einen anderen Stream zu transformieren, er ist kein Endpunkt. Normalerweise würden Sie also etwas wie someObs.map(s => s.name).subscribe() zurückgeben. Wenn Sie nur etwas tun möchten, können Sie stattdessen den Operator do() verwenden.

Aber in Ihrem Fall, wenn Daten === 'someValue', würden Sie zuerst zu Route1 im Kartenoperator navigieren. Dann wird der Rückruf des Abonnements (next()) ausgelöst und der Router navigiert zu "home".

Um die Funktionalität zu erhalten Sie beschrieben Sie es als solches tun könnte:

SomeComponent:

constructor(
    private someService: SomeService, 
    private router: Router 
) { } 

ngOnInit() { 
    this.someService.getSomeData().subscribe({ 
     next:() => { 
     console.log("getSomeData() did not route to a new link! Routing to a 3rd link"); 
     this.router.navigate(['home']); 
     }, 
     complete:() => {}, 
     error: (err) => { 
     console.log("Some error occurred.", err); 
     } 
    }); 

Someservice:

constructor(
    private router: Router, 
    private someOtherService: SomeOtherService 
) { } 

getSomeData() { 
    return Observable.create(observer => { 
    this.someOtherService.getSomeOtherData().first().subscribe(data => { 
     if (data === 'someValue') { 
     console.log("The data was 'someValue'"); 
     this.router.navigate(['route1']); 
     } 
     else if (data == 'someOtherValue') { 
     console.log("The data was 'someOtherValue'"); 
     this.router.navigate(['route2']);  
     } else { 
     // none of the options above. call next() on the observer so 
     // it will route to 'home' 
     observer.next(); 
     } 
     observer.complete(); 

    }); 
}); 


} 
+0

Diese Idee, die vollständige() Methode des Beobachters als leeren Halt für den beobachtbaren Stream zu verwenden, funktioniert. Ich stellte jedoch fest, dass die Antwort von meriton besser ist, da das gesamte Routing schließlich vom Beobachter selbst gehandhabt wird und die Observablen einfach die notwendigen Datenwerte an den Beobachter zurückgeben. – yoonjesung