Can I use one-time subscription to http action inside an Angular service? Is there any disadvantage of doing so?
public async signInAsync(userName: string, password: string): Promise<void> {
try {
const token = await this.authenticate(userName, password).pipe(first()).toPromise();
this.signInCompleted(token);
} catch {
this.signInFailed();
}
}
In order to use OnPush change detection strategy and also keep "business logic" inside services, services use observable called state$
to expose values to components Components then subscribes to this value with async pipe. Only functions of service are able to update the state by calling this.patchState('actionName', {...})
.
protected signInCompleted(token: string): void {
this.localStorageService.setItem(LocalStorageKey.AUTH_TOKEN, token);
this.patchState('signInCompleted', {
isAuth: true,
token: token,
error: null
});
this.router.navigate([AppRoute.AUTH]);
}
Therefore, if I use HttpClient, I have to somehow subscribe to returned observable.
I've started with simple subscription:
protected signIn(...): void {
this.authenticate(..).subscribe(..);
}
But then, I've realized that it's not testable, because I don't know when the call is executed and async()
doesn't know about observable.
To keep it testable, I had to make it async and convert to promise, however I'm not sure if there is any disadvantage if I subscribe with pipe(first()).toPromise()
.
I was also thinking about using pipe(map(...)).pipe(catchError(...))
, but I don't know how then bind the action to component, or whether its better then the previous approach.
public signIn(userName: string, password: string): Observable<void> {
return this.authenticate(userName, password).pipe(map(...)).pipe(catchError(...));
}
signInAsync
? What should be returned to your component? You should usually be able to do everything with Observables only. – fridopipe
you can just add all the operators to onepipe
. To perform side effect like updating a shared state you can use thetap
operator.this.authenticate(userName, password).pipe(tap(...), catchError(...));
– fridoexport function effect<T>( completed?: (value: T) => void, failed?: (error: any) => void ): OperatorFunction<T, void> { return (observable$: Observable<T>): Observable<void> => observable$.pipe( tap(completed, failed), catchError(_ => of()), map(() => {}) ); }
– bojo