typescript - Angular rxjs: mantener la suscripción después de que se produce un error
Necesito saber cómo mantener la suscripción después de que se haya producido un error.
He creado este observable:
let saveClick$ = Observable.fromEvent(this.saveButton.nativeElement, 'click');
Entonces, he intentado con Observable.empty, pero hasta donde he podido averiguar, emite un complete, por lo que el suscriptor será eliminado:
this.saved$ = saveClick$.pipe(this.pushUser(), catchError(() => Observable.empty<AdministrationUser>()), share());
Quiero que cuando se alcance cualquier error, la suscripción se mantenga.
¿Alguna idea?
Código adicional:
// Custom pipes
private push = <T>() => switchMap<T, AdministrationUser>(() => this.service.push(this.user));
private handleError = <T>() => catchError<T, Array<{code: string, message: string}>>((error: ResponseError) => Observable.of(error.errors));
private handleEmptyUser = <T>() => catchError<T, AdministrationUser>(() => Observable.of(UsuarisadministracioSubcomponentComponent.EMPTY_USER));
private pushUser = () => pipe( //simplified pipe
this.push()
);
Y mi servicio es:
const buildURL = () => map((filter: {userId: string} & Trace) => this.buildPushURL(filter.currentUser, filter.currentApplication, filter.userId));
const makeRequest = () => switchMap((url: string) => httpMethodFn.call(this.authHttp, url, user));
const buildResponse = () => map(() => user);
const onErrorGetDetails = () => catchError((error: Response) => Observable.throw(<ResponseError>error.json()));
return Observable.of({userId: user.id})
.pipe(
buildURL(),
makeRequest(),
buildResponse(),
onErrorGetDetails()
);
Respuesta #1
Deberías capturar el error en el pipeline observable interno en lugar del observable fuente. Si capturas el error en el observable fuente, entonces según el concepto de rxjs el observable fuente no emitirá nuevos valores después de que ocurra un error. Así que captura el error en el observable interno de esta manera:
this.saved$ = saveClick$.pipe(
//i am assuming this.pushUser() is a higher order function which returns an observable
this.pushUser()
.pipe(
catchError(() => Observable.empty<AdministrationUser>())
),
share()
);
Capturar el error en el observable interno mantendrá tu observable fuente activo y seguirá emitiendo los valores incluso si tu observable interno lanza un error. Puedes mover tu operador share() según tu necesidad [es decir, ya sea dentro de tu observable interno o en la línea de tubería del observable fuente], pero la idea sigue siendo la misma: capturar el error dentro de la tubería del observable interno para mantener vivo tu observable fuente (externo).
EDICIÓN 1: [Sugiriendo un código simplificado según el último código del usuario]
Por favor, defina su método push en su servicio de esta manera:
push(user) {
//By seeing your code; I could not figure out where and how are you passing the parameter in buildUrl
//It appears to me that this.buildPushURL method simply return an URL based on the passed parameters and IT DOES NOT MAKE
//ANY HTTP CALL TO YOUR BACKEND. IF THAT IS TRUE -
//THEN please adjust the below code accordingly as I dont know how you use this method OR PLEASE provide some more detaling
//on this.buildPushURL() method
const url = this.buildPushURL(filter.currentUser, filter.currentApplication, filter.userId);
//I am assuming that httpMethodFn calls http.get() or http.post() and returns an observable
//Suggestion - Why you dont use httpClient.get or httpClient.post() directly? It will avoid to call
//httpMethodFn by httpMethodFn.call?
//Bottom line is - httpMethodFn.call must return an observable otherwise adjust your httpMethodFn.call code to return an observable
//to make below code work.
return httpMethodFn.call(this.authHttp, url, user)
.pipe(
map(user => {
console.log(user);
return user;
}),
catchError(error => throwError(<ResponseError>error.json()))
);
}
Y úsalo así -
yourMethodReturnsAnObservableWhichIsSubscribeByTheConsumer() {
const saveClick$ = Observable.fromEvent(this.saveButton.nativeElement, 'click');
return saveClick$.pipe(
switchMap(() => {
return this.push(user)
.pipe(
//you catch error here so that your outer observable will keep alive
//please adjust you code as per your need
//I guess it will give you an idea
handleError()
)
})
);
}
Espero que tengas una idea de cómo simplificar tu código. No pude proporcionar una solución perfecta ya que no conozco tu diseño y el código completo. Estoy bastante seguro de que el código anterior te dará una idea.
Una sugerencia: es bueno usar la función de orden superior, pero entiende que no debes usarlas en exceso ya que hace que tu código sea un poco difícil de entender [PD: es mi opinión personal y es bueno tener opiniones diferentes.. :)]. Mantén tu cadena observable lo más simple posible aplicando el estilo funcional de programación. RXJS es increíble :)
Respuesta #2
Necesitas usar el operador catchError y devolver el Observable NEVER
import { NEVER } from 'rxjs';
import { catchError } from 'rxjs/operators';
observable$.pipe(catchError((error: any) => {return NEVER;}));
