Angular: Unlock Mastery of Side Effects with @Effect and NgRx – Essential 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 file.
@Injectable({
providedIn: 'root'
})
export class PostService {
constructor(
private serverService: ServerService,
private store: Store<AppState>
) {}
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);
}
As you can see there are four methods getPosts, addPost, removePost, updatePost. ServerService is based on standard GET/POST/DELETE/PUT HTTP methods.
In this case POSTS_URL is const string with URL to endpoint like ‘http://api.domain.com/posts’.
@Effect() based CRUD code snippets
@Effect() – Get list (posts)
How to use @Effect for Get action
@Effect()
loadPosts$: Observable<Action> = 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)))
);
})
);
In case of 7th line in our code ( switchMap((response: Response) => [ ) we could use Destructuring Assignment with TypeScript Static Typing as well.
@Effect() – Create (post)
How to use @Effect for Create action
@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)
How to use @Effect for Update action
@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)
How to use @Effect for Delete action
@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)))
);
})
);