import { Post } from './models/post.model';
import { AppActions, SET_POSTS } from './app.actions';
export interface State {
posts: Post[]
}
const initialState: State = {
posts: []
}
export function appReducer(state = initialState, action: AppActions) {
switch(action.type) {
case SET_POSTS:
return {
...state,
posts: action.payload
}
default:
return state;
}
}
export const getPosts = (state: State) => state.posts;
So this is what I have on the reducer end, I don't think there's anything wrong with it I wrote it from the examples provided on the official documentation.
ngOnInit() {
this.posts$ = this.store.select(fromPost.getPosts);
this.postService.getPosts();
}
Now, the postService is working and I have been able to confirm this by logging it, but for some reason it never gets populated.
.subscribe((posts: Post[]) => {
this.store.dispatch(new AppAction.SetPosts(posts));
}
When I log posts, I get the expected output, so I am wondering if it's the dispatch that's being improperly called or handled.
import { Action } from '@ngrx/store';
import { Posts } from './models/post.model';
export const SET_POSTS = 'Set Posts';
export class SetPosts implements Action {
readonly type = SET_POSTS;
constructor(public payload: Post[]) {}
}
export type AppActions = SetPosts;
This is the post actions, so I don't see anything wrong with it or is there?
Update:
this.posts$ = this.store.select(fromPost.selectPosts);
this.postService.getPosts();
console.log(this.posts$);
this.posts$.subscribe( value => console.log(value));
//Object { _isScalar: false, actionsObserver: {…}, reducerManager: {…}, source: {…}, operator: {…} }
//undefined
I also made some changes in the reducer:
const getPosts = (state: State) => state.posts;
export const selectState = (state: State) => state;
export const selectPosts = createSelector(
selectState,
getPosts
);
I am still getting nothing when I do this in my html file:
<div *ngIf="(posts$ | async) as posts">
<div *ngFor="let post of posts">
{{ post.title}}
</div>
</div>
Short answer, you have write a selector to bind the post to the UI.
Create Selector.ts file
import { createSelector } from '@ngrx/store';
import * as fromPost from 'post.reducer'
export const selectState = (state: State) => state;
export const selectPost = createSelector(
selectState,
fromPost.getPosts
);
In your component
constructor() {
this.store.dispatch(new LoadPost())
}
ngOnInit() {
this.posts$ = this.store.select(selectPost);
}
Create an effect that calls postService
, async services are side effects to the store and should be handled through Effects.
Documentation for Setting up effects
create posts action.ts
import { Action } from '@ngrx/store'
export enum PostActionTypes {
LoadPost = '[Post] Load Post',
SetPosts = '[Post] Load Post Success',
LoadPostFailure = '[Post] Load Post Failure',
}
export class LoadPost implements Action {
readonly type = PostActionTypes.LoadPost
}
export class SetPosts implements Action {
readonly type = PostActionTypes.SetPosts
constructor(public payload: Post[]) {}
}
export class LoadPostFailure implements Action {
readonly type = PostActionTypes.LoadPostFailure
constructor(public payload: any) {}
}
export type PostActions =
| LoadPost
| SetPosts
| LoadPostFailure
create Post effect.ts
import { Injectable } from '@angular/core'
import { Actions, ofType } from '@ngrx/effects'
import { of } from 'rxjs'
import { catchError, map, switchMap } from 'rxjs/operators'
@Injectable()
export class PostEffects {
constructor(private postService: PostService, private actions$: Actions) {}
@Effect()
loadPosts$ = this.actions$.pipe(
ofType(PostActions.LoadPost),
switchMap(() => {
return this.postService.getPosts().pipe(
map(posts => new SetPosts(posts)),
catchError(error => of(new LoadPostFailure(error)))
)
})
)
}
NOTE: Effects are Injectable classes, make sure you add them to
NgModule({
declarations: [],
imports: [
...
EffectsModule.forRoot([ ... effects ]), // Effect classes as CSV
...
]
})