Angular - Side effects with @effect and ngrx - CRUD snippets

Angular is giving great tools to build application and NgRx gives you opportunity to manage you app with state. In this article I'm sharing few code snippets which will help you to bring @Effects to work with CRUD application.

Actions

In post.actions.ts file we are creating actions like

import {Action} from '@ngrx/store';

export enum PostActionTypes {
  FetchPosts = '[POST] Fetch Posts',
  FetchPostsError = '[POST] Fetch Posts Error',
  SetPosts = '[POST] Set Posts',

  CreatePost = '[POST] Create Post',
  CreatePostError = '[POST] Create Post Error',
  CreatePostSuccess = '[POST] Create Post Success',

  UpdatePost = '[POST] Update Post',
  UpdatePostError = '[POST] Update Post Error',
  UpdatePostSuccess = '[POST] Update Post Success',

  RemovePost = '[POST] Remove Post',
  RemovePostError = '[POST] Remove Post Error',
  RemovePostSuccess = '[POST] Remove Post Success',

  FetchCurrentPost = '[POST] Fetch Current Post',
  SetCurrentPost = '[POST] Set Current Post',
}

export class FetchPosts implements Action {
  readonly type = PostActionTypes.FetchPosts;

  constructor() {
  }
}

export class FetchPostsError implements Action {
  readonly type = PostActionTypes.FetchPostsError;

  constructor(public payload) {
  }
}

export class SetPosts implements Action {
  readonly type = PostActionTypes.SetPosts;

  constructor(public payload) {
  }
}

export class CreatePost implements Action {
  readonly type = PostActionTypes.CreatePost;

  constructor(public payload) {
  }
}

export class CreatePostError implements Action {
  readonly type = PostActionTypes.CreatePostError;

  constructor(public payload) {
  }
}

export class CreatePostSuccess implements Action {
  readonly type = PostActionTypes.CreatePostSuccess;

  constructor(public payload) {
  }
}

export class RemovePost implements Action {
  readonly type = PostActionTypes.RemovePost;

  constructor(public payload) {
  }
}

export class RemovePostError implements Action {
  readonly type = PostActionTypes.RemovePostError;

  constructor(public payload) {
  }
}

export class RemovePostSuccess implements Action {
  readonly type = PostActionTypes.RemovePostSuccess;

  constructor(public payload) {
  }
}

export class UpdatePost implements Action {
  readonly type = PostActionTypes.UpdatePost;

  constructor(public payload) {
  }
}

export class UpdatePostError implements Action {
  readonly type = PostActionTypes.UpdatePostError;

  constructor(public payload) {
  }
}

export class UpdatePostSuccess implements Action {
  readonly type = PostActionTypes.UpdatePostSuccess;

  constructor(public payload) {
  }
}

export class FetchCurrentPost implements Action {
  readonly type = PostActionTypes.FetchCurrentPost;

  constructor() {
  }
}

export class SetCurrentPost implements Action {
  readonly type = PostActionTypes.SetCurrentPost;

  constructor(public payload) {
  }
}

export type PostAction =
  FetchPosts |
  FetchPostsError |
  SetPosts |
  CreatePost |
  CreatePostError |
  CreatePostSuccess |
  RemovePost |
  RemovePostError |
  RemovePostSuccess |
  UpdatePost |
  UpdatePostError |
  UpdatePostSuccess |
  FetchCurrentPost |
  SetCurrentPost;

So easily said every action has it's invoker like 'UpdatePost' than if action fails the 'UpdatePostError' should be invoked and if Update will succeed 'UpdatePostSuccess' should be invoked.

Service

Post service in post.service.ts


@Injectable({
  providedIn: 'root'
})
export class PostService {
  constructor(
    private serverService: ServerService,
    private store: Store
  ) {}

  getPosts = () => this.serverService.get(POSTS_URL);
  addPost = (post) => this.serverService.post(POSTS_URL, post);
  removePost = (post) => this.serverService.delete(POSTS_URL + post._id);
  updatePost = (post) => this.serverService.put(POSTS_URL + post._id, post);
}

@Effect() based CRUD code snippets

@Effect() - Get list (posts)

@Effect()
  loadPosts$: Observable = this.actions$.pipe(
    ofType(PostActionTypes.FetchPosts),
    switchMap(() => {
      return this.postService.getPosts()
        .pipe(
          switchMap((response: Response) => [
            new SetPosts(response.data),
            new StopLoading({error: null, msg: 'Posts loaded'})
          ]),
          catchError(err => of(new FetchPostsError(err)))
        );
    })
  );

@Effect() - Create (post)

@Effect()
  createPosts$: Observable = this.actions$.pipe(
    ofType(PostActionTypes.CreatePost),
    switchMap((action) => {
      return this.postService.addPost(action['payload'])
        .pipe(
          switchMap((response: Response) => [
            new CreatePostSuccess(response.data),
            new StopLoading({error: null, msg: 'Post loaded'})
          ]),
          catchError(err => of(new CreatePostError(err)))
        );
    })
  );

@Effect() - Update (post)

@Effect()
  updatePost$: Observable = this.actions$.pipe(
    ofType(PostActionTypes.UpdatePost),
    switchMap((action) => {
      return this.postService.updatePost(action['payload'])
        .pipe(
          switchMap((response: Response) => [
            new UpdatePostSuccess(response.data),
            new StopLoading({error: null, msg: 'Post updated'})
          ]),
          catchError(err => of(new UpdatePostError(err)))
        );
    })
  );

@Effect() - Delete (post)

@Effect()
  removePost$: Observable = this.actions$.pipe(
    ofType(PostActionTypes.RemovePost),
    switchMap((action) => {
      return this.postService.removePost(action['payload'])
        .pipe(
          switchMap((response: Response) => [
            new RemovePostSuccess(response.data),
            new StopLoading({error: null, msg: 'Post removed'})
          ]),
          catchError(err => of(new RemovePostError(err)))
        );
    })
  );