While working with ngrx, sometimes we need to be notified after an action has been dispatched successfully in order to perform some kind of action (e.g: open or close a modal from a component).
One of the solution would be creating a Subject
in the facade class and emit a value in the effect after the success action has been dispatched:
export class UserFacade {
readonly userAdded$: Subject<boolean> = new Subject<boolean>();
constructor(private readonly store: Store) {}
addUser(user: UserPayload): void {
this.store.dispatch(addUser({ user }));
}
}
Our effect would look like this:
@Injectable()
export class UserEffects {
addUser$ = createEffect(() =>
this.actions$.pipe(
ofType(addUser),
switchMap(({ payload }) =>
this.userDataService.addUser(payload).pipe(
map(() => addUserSucceeded()),
tap(() => this.userFacade.userAdded$.next(true)),
catchError((error) => of(addUserFailed(error)))
)
)
)
);
constructor(
private readonly actions$: Actions,
private readonly userFacade: UserFacade,
private readonly userDataService: UserDataService
) {}
}
This solution is almost ideal. We don't need to directly subscribe to the effect and we can expose the information if action has been dispatched using facade pattern. On other hand, we have to create a Subject
in the facade and control it in the effect. Instead, we can utilize an injectable service that provides an observable stream of all actions and listen on the success action:
export class UserFacade {
readonly userAdded$: Observable<boolean> = this.actions$.pipe(
ofType(addUserSucceeded),
mapTo(true)
);
}
With this solution, we don't have to create additional class member and emit anything in the effect. Now, we can use it in our component:
@UntilDestroy()
@Component({ ... })
export class UserComponent {
constructor(
private readonly userFacade: UserFacade,
private readonly matDialogRef: MatDialogRef<UserComponent>
) {}
confirmClicked(): void {
this.userFacade.addUser({ ... })
this.userFacade.userAdded$
.pipe(
filter((added) => added),
take(1),
untilDestroyed(this)
)
.subscribe(() => this.matDialogRef.close());
}
}
If you have any questions, feel free to ask them in the comments!
Top comments (0)