In angular, you have normally components and the components will call your services. the services are all async process. so you will call your service in your component and you will subscribe to the observable and get your response. there you can act base on your response. Assume it is a logging process and you want to show if the process was successful or not. But I'm using ngrx. and in ngrx structure, you will dispatch an action. after the action is triggerd you will probably have an effect which will call your backend and based on the response will raise another success or error action. this action than will update the state in your reducers.
Than you will have some state and this are observable and you can subscribe to them. I want the subscription only raise if I take an action. But now because of my subscription every-time that I load the page it get triggered, so I added an active property in my object and set it by raise to true and after I get the response to false. This is working now, but I am not happy with the process. I will also write the code here, and will be happy if sombody can have a better way to solve this problem:
export class ActionResponse {
public Type: ResponseType = ResponseType.Failed;
public Message: string = '';
public Active: boolean = false;
constructor(active:boolean = false, type: ResponseType = ResponseType.Failed, message: string = '') {
this.Active = active;
this.Type = type;
this.Message = message;
}
}
In State.ts:
export class CustomerState {
public customer: Customer = new Customer();
public isLoggedIn: boolean = false;
public updatePasswordActionResponse: ActionResponse = new ActionResponse();
}
In action.ts
export const CustomerUpdatePasswordAction = createAction('[Customer] Update Password Customer', props<{ email:string, oldPassword: string, newPassword:string }>());
export const CustomerUpdatedPasswordSuccessAction = createAction("[Customer] Updated Password Success", props<{customer:Customer, actionResponse: ActionResponse}>());
export const CustomerUpdatedPasswordErrorAction = createAction('[Customer] Update Password Error', props<ActionResponse>());
In effects.ts:
UpdateCustomerPassword$ = createEffect(() =>
this.actions$.pipe(
ofType(customerActions.CustomerUpdatePasswordAction),
switchMap((data: { email: string, oldPassword: string, newPassword:string}) =>
this.customerService.updateCustomerPassword(data.email, data.oldPassword, data.newPassword).pipe(
switchMap((cus: Customer) => [customerActions.CustomerUpdatedPasswordSuccessAction({customer:cus, actionResponse:new ActionResponse(true, ResponseType.Successfull)})]),
catchError((response: ErrorResponse) => of(customerActions.CustomerUpdatedPasswordErrorAction(new ActionResponse(true, ResponseType.Failed, response.message))))
)
)
)
)
In reducers.ts:
on(customerActions.CustomerUpdatedPasswordSuccessAction, (state: CustomerState, data:{customer:Customer, actionResponse: ActionResponse}) => ({
...state,
isLoggedIn: true,
customer: data.customer,
updatePasswordActionResponse: data.actionResponse
})),
on(customerActions.CustomerUpdatedPasswordErrorAction, (state: CustomerState, actionResponse: ActionResponse) => ({
...state,
updatePasswordActionResponse: actionResponse
}))
on(customerActions.ResetCustomerResponseAction, (state: CustomerState) => ({
...state,
loadActionResponse: new ActionResponse(),
createActionResponse: new ActionResponse(),
updateActionResponse: new ActionResponse(),
updatePasswordActionResponse: new ActionResponse(),
toggleActionResponse: new ActionResponse()
}))
In my component: A: dispatch:
onPasswordSubmit(form: NgForm) {
if(form.valid) {
this.store.dispatch(CustomerUpdatePasswordAction({email:this.customer.Email, oldPassword: this.oldPassword as string, newPassword: this.newPassword}));
}
}
B: Reflect the response to the user:
this.customerUpdatePasswordSubscription = this.customerUpdatePasswordResponse$.subscribe((actionResponse: ActionResponse) => {
if(actionResponse.Active)
{
if(actionResponse.Type === ResponseType.Failed)
{
this.notifyService.showError( actionResponse.Message, "Could not update password");
this.store.dispatch(ResetCustomerResponseAction());
}
else
{
this.notifyService.showSuccess( "Your password was successfully changed", "Changed Password");
this.store.dispatch(ResetCustomerResponseAction());
}
}
});
The fact you are manually subscribing to your selector via customerUpdatePasswordResponse$
is generally an antipattern and a hint that you should be doing something else. I would handle the notifications in effects, then you can simplify the associated state - active
should no longer be required.
Effects:
showSuccessNotification$ = createEffect(() =>
this.actions$.pipe(
ofType(customerActions.CustomerUpdatedPasswordSuccessAction),
tap(() => this.notifyService.showSuccess("Your password was successfully changed", "Changed Password"))
),
{ dispatch: false }
);
showSuccessNotification$ = createEffect(() =>
this.actions$.pipe(
ofType(customerActions.CustomerUpdatedPasswordSuccessAction),
tap(() => this.notifyService.showError(actionResponse.Message, "Could not update password"))
),
{ dispatch: false }
);
There is possibly no need to store the errors in the store at all now and thus no need to reset them either, but worth noting that in my experience errors are often left in the store until a new operation is performed.
A couple of other things you may want to consider: