Introduction:
I have an Angular application with NGXS as a state management pattern and Angular Material for the UI components (such as snackbars for notifications). The application has several different action which I want to dispatch at once when the user clicks the save button. Afterwards I want to inform the user if all actions were successfully dispatched.
Problem:
Even if one action fails, I still get the message 'Changes were successfully saved', but the action itself logs the occurred errors. Is there a way to handle errors in the save function differently?
action 1 - uploadAvatar
@Action(UploadAvatar)
public uploadAvatar(ctx: StateContext<AuthStateModel>, action: UploadAvatar) {
return this.uploadService.uploadAvatar(ctx.getState().token, action.avatar).pipe(
tap((result: any) => {
console.log(result);
}),
catchError((error: HttpErrorResponse) => {
console.error(error);
return of(ctx.patchState({ responseStatus: error.status }));
})
);
}
action 1 - updateUser
@Action(UpdateUser)
public updateUser(ctx: StateContext<AuthStateModel>, action: UpdateUser) {
return this.userService.updateUser(ctx.getState().token, action.user).pipe(
tap((result: any) => {
console.log(result);
}),
catchError((error: HttpErrorResponse) => {
console.error(error);
return of(ctx.patchState({ responseStatus: error.status }));
})
);
}
save function
public save() {
try {
this.store.dispatch(new UploadAvatar(this.avatar));
this.store.dispatch(new UpdateUser(this.user));
this.snackbar.showSnackbar('Changes were successfully saved');
} catch (error) {
this.snackbar.showSnackbar('Changes could not be saved');
}
}
You are catching the error in rxjs using catchError
, so it will never reach your try/catch
. Also the dispatch are async, so it will always show the showSnackbar
with the success message. This might work better.
Untested code, but maybe you'll understand the idea better
public save(): void {
this.store.dispatch([
new UploadAvatar(this.avatar),
new UpdateUser(this.user)
]).subscribe({
next: () => this.snackbar.showSnackbar('Changes were successfully saved'),
error: () => this.snackbar.showSnackbar('Changes could not be saved')
});
}
@Action(UploadAvatar)
public uploadAvatar(ctx: StateContext<AuthStateModel>, action: UploadAvatar) {
return this.uploadService.uploadAvatar(ctx.getState().token, action.avatar).pipe(
catchError((error: HttpErrorResponse) => {
ctx.patchState({ responseStatus: error.status });
return throwError(error);
})
);
}
@Action(UpdateUser)
public updateUser(ctx: StateContext<AuthStateModel>, action: UpdateUser) {
return this.userService.updateUser(ctx.getState().token, action.user).pipe(
catchError((error: HttpErrorResponse) => {
ctx.patchState({ responseStatus: error.status });
return throwError(error);
})
);
}
Basically, you catch the error in your state, write the response status to the state, and re-throw the same error, which can be caught with the dispatch