Reputation: 1205
I am working with Angular/rxjs 6. I am having a hard time trying to get an observable sequence to run correctly. Here is the pseudo code for what I am trying to accomplish.
Receive a request to change systems:
I am not sure if my current approach is correct. It does not seem elegant because there is a lot going on and it is hard to follow. Some of this works but the response from the last http call does not return to the .toPromise().then()
.
Component:
changeSystem(systemID: string) {
if(! this._service.changeSystem(systemID))
this.toastError("A problem occurred");
}
Service:
public changeSystem(systemID: string): boolean {
if (!systemID) {
this.log("System ID is missing");
return false;
}
if(this.currentSystemID === systemID) {
return true;
} else {
this.setNewSystem(systemID).toPromise()
.then(success => { // <-- the code here never runs
if (success)
this.router.navigate(['/dashboard/', systemID])
else
return false;
}
}
}
private setNewSystem(systemID: string): Observable<boolean> {
if (!this.isAuthorized(systemID)) {
this.log('system is not authorized for user');
return Observable.of(false);
}
return this.setAlphaSystem(systemID).pipe(
concatMap(alphaResult => {
if (!alphaResult) {
this.log('failed to set alpha system');
return Observable.of(false);
} else {
return this.setBetaSystem(systemID);
}
}));
}
private setAlphaSystem(systemID: string): Observable<boolean> {
return this.alphaSystemRootUrl$.pipe(
concatMap(alphaUrl => {
const url = alphaUrl + 'pages/' + systemID + '/Home/ChangeSystem/';
return this.http.get(url, { headers: { 'Anonymous': 'true' }, withCredentials: true })
.map(data => {
let response = JSON.parse(JSON.stringify(data));
if (response.error) {
return false;
} else {
return true;
}
}).catch(err => {
this.log('Failed change system request on alpha: ' + JSON.stringify(err));
return Observable.of(false);
});
}));
}
private setBetaSystem(systemID: string): Observable<boolean> {
return this.changeBetaSystem(systemID).map(systemData => {
this.saveSystemData(systemData);
return true;
}).catch((err: ErrorInfo) => {
this.log('Failed change system request on beta: ' + err.message);
return Observable.of(false);
});
}
private changeBetaSystem(systemID: string): Observable<SystemData> {
const json = JSON.stringify(systemID);
return this.http.post<SystemData>("api/System/ChangeSystem", json, this.httpOptions)
.pipe(catchError(new ErrorInfo().parseObservableResponseError));
}
Do I need a switchmap in setBetaSystem instead of map?
I saw some examples where the component subscribed to a behavior subject that the service functions updated as the processing continued. Should I try that?
Any help is much appreciated!
Upvotes: 0
Views: 1124
Reputation: 1205
After a whole lot of trial and error I got a working solution using another answer as an idea to keep track of the result through a subject.
https://stackoverflow.com/a/60670835/738690
It is not very pretty but it at least it works.
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { map } from 'rxjs/operators';
class service {
/*
WORKING mock http observables:
private changeSystemOnAlpha = of({"success":"true"});
private changeSystemOnBeta = of({"name":"system1", "url":"http://goferit"});
ERROR mock http observables:
private changeSystemOnAlpha = throwError('OOPS');
private changeSystemOnBeta = throwError('OHNOES');
*/
private changeSystemOnAlpha = of({"success":"true"}); //mock http
private changeSystemOnBeta = of({"name":"system one", "url":"http://goferit"}); //mock http
private currentSystemID: string = null;
// these members are for tracking the progress of a system change
private setSystemStatus = new BehaviorSubject<boolean>(true);
private get setSystemStatus$() {
return this.setSystemStatus.asObservable();
}
private setSystem(systemID: string): Observable<boolean> {
console.log('service.setSystem-> set system request for [' + systemID + ']');
// first call to alpha
this.changeSystemOnAlpha.subscribe(result => {
// log
console.log('service.setSystem-> System changed on alpha to [' + systemID + ']');
// next call beta
this.changeSystemOnBeta.subscribe(system => {
// log, save, emit true
console.log("service.setSystem-> System changed on beta to [" + systemID + "]");
this.saveSystemData(system);
this.currentSystemID = systemID;
this.setSystemStatus.next(true);
}, error => {
console.log('service.setSystem-> An error occured changing the system on the beta host: ' + error);
this.setSystemStatus.next(false);
});
}, error => {
console.log('auth.service.setSystem-> An error occured changing the system on the alpha host: ' + error);
this.setSystemStatus.next(false);
});
// return ref to behavior/observable
return this.setSystemStatus$;
}
public changeSystem(systemID: string, navigate: boolean = false): Observable<boolean> {
console.log("service.changeSystem-> Request to change system to [" + systemID + "] made using the current system of [" + this.currentSystemID + "]");
// see if we are actually changing the system, check params, get current system and compare
if (!systemID) {
console.log("service.changeSystem-> request made using null system");
return of(false);
}
if (!this.currentSystemID || this.currentSystemID.toLowerCase() !== systemID.toLowerCase()) {
return this.setSystem(systemID).pipe(map(success => {
console.log("service.changeSystem-> auth.service.setsystem([" + systemID + "]) result is [" + success + "]");
if (success && navigate) {
console.log("service.changeSystem-> router.navigate to dashboard/[" + systemID + "]");
this.router.navigate(['/dashboard/', systemID])
}
// we are done
return success;
}));
} else {
// dont need to change systems
return of(true);
}
}
private saveSystemData(data) {
console.log("service.saveSystemData-> saving system data for [" + data.name + "]");
}
}
// Testing like we are calling service from components over time
let testService = new service();
let systemRequests = ["10","","10","20"];
let delay = 0;
const appDiv: HTMLElement = document.getElementById('app');
for(let systemID of systemRequests) {
delay+=500;
setTimeout( () =>
testService.changeSystem(systemID).subscribe(success => {
console.log("changeSystem('"+systemID+"') resulted in ["+success+"]");
if (success)
appDiv.innerHTML = systemID;
})
, delay );
}
Here is my stackblitz.
Upvotes: -1