Search code examples
angulartypescriptasynchronousobservablengxs

NGXS, best way to dispatch start, success or failure actions from the same action?


I have the following code for fetching all of my posts

`    @Action(actions.FetchPosts)
    fetchAll(ctx: StateContext<PostStateModel>){
        return this.postService.fetchPosts().pipe(tap((postsResults) => {
            const state = ctx.getState();
                ctx.patchState({
                ...state,
                posts: [                    
                    ...postsResults                    
                ],                
            })
        }),
        mergeMap(() => {
          console.log("Inside of mergeMap")
          return ctx.dispatch(new actions.FetchPostsSuccess())
        }),      
        catchError(err => 
        {
          console.log("Inside of catchError")
          return ctx.dispatch(new actions.FetchPostsFail(err))
        }
      ))
    }`

This works, however, I want to perform another dispatch prior to calling my post service. What is the best way using async programming to make sure I first dispatch the start action which will set the loading property to true, and then ensure that I only call success once the request returns successfully.

Also, following the ngxs docs, I am using mergeMap() which seems to get the job done. I'm curious though, does the postService which returns an array of posts return a single observable or a single observable which then emits multiple higher order observables (inner observables)?


Solution

  • You could start with dispatching the loading action:

    @Action(actions.FetchPosts)
    fetchAll(ctx: StateContext<PostStateModel>){
        return ctx.dispatch(new ChangeLoadingToInProgress()).pipe(
          mergeMap(() => this.postService.fetchPosts()),
          tap((postsResults) => {
            const state = ctx.getState();
                ctx.patchState({
                ...state,
                posts: [                    
                    ...postsResults                    
                ],                
            })
        }),
        mergeMap(() => {
          console.log("Inside of mergeMap")
          return ctx.dispatch(new actions.FetchPostsSuccess())
        }),      
        catchError(err => 
        {
          console.log("Inside of catchError")
          return ctx.dispatch(new actions.FetchPostsFail(err))
        }
      ))
    }
    

    or I believe you could just change the state right away:

    @Action(actions.FetchPosts)
    fetchAll(ctx: StateContext<PostStateModel>){
        ctx.patchState({ loading: true });
        return this.postService.fetchPosts().pipe(tap((postsResults) => {
            const state = ctx.getState();
                ctx.patchState({
                ...state,
                posts: [                    
                    ...postsResults                    
                ],                
            })
        }),
        mergeMap(() => {
          console.log("Inside of mergeMap")
          return ctx.dispatch(new actions.FetchPostsSuccess())
        }),      
        catchError(err => 
        {
          console.log("Inside of catchError")
          return ctx.dispatch(new actions.FetchPostsFail(err))
        }
      ))
    }