Search code examples
angularngrxngrx-effectsngrx-store

ngrx/store effects nested objects


I'm learning Angular 2 and i'm trying to user ngrx/store, but i have some difficulties with some special cases.

Example I'm trying to remove a parent object. What i want to do, is to remove also child objects.

Here are my entities :

export class Discussion {
  id: string;
  name: string;
  createdAt: Date;
  posts: Post[];
}

export class Post {
  id: string;
  title: string;
  data: string;
  createdAt: Date;
  comments: Comment[];
}

export class Comment {
  id: string;
  data: string;
  createdAt: Date;
}

I'm using normalizr to flatten my state so my stored discussion will look like:

{
  id: "1",
  name: "First dicussion",
  createdAt: "...",
  posts: ["1", "2", "3", "5"]
}

I have 3 reducers, One reducer for Discussions, another one for Posts and the last for Comments. All reducers handles delete Action of it own type. Here is an example of Discussion reducer:

export function reducer(state = initialState, action: discussion.Actions): State {
switch (action.type) {
    case discussion.REMOVE: {
        const idToRemove = action.payload;
        const newEntities = state.entities;
        delete newEntities[idToRemove];
        return Object.assign({}, state, {
            entities: newEntities
        });
    }
}}

My action look like this :

export class RemoveAction implements Action {
readonly type = REMOVE;

/**
 * Constructor
 * @param payload The id of the discussion to remove
 */
constructor(public payload: string) { }
}

When I delete a discussion, I want to delete posts related to the discussion and posts effect will delete comments related to the deleted posts. I used effects of ngrx do do that so I used this effect :

@Effect()
removeDiscussion: Observable<Action> = this._actions
.ofType(dicussion.REMOVE)
.map((action: discussion.RemoveAction) => action.payload)
.mergeMap(discId => {

    // How to get posts from discussion id ???

    // Fire related Actions
    return [
        new posts.RemoveAction(postsToRemove)
    ];
});

My question is how to get posts to remove from an Id of discussion ?

Thanks for reading.


Solution

  • You can access the store in an effect by using withLatestFrom.
    (import 'rxjs/add/operator/withLatestFrom';)

    Inject the store in the effects-class:

    constructor(private _actions: Actions, private store: Store<fromRoot.State>)
    

    Use it in the effect:

    @Effect()
    removeDiscussion: Observable<Action> = this._actions
        .ofType(dicussion.REMOVE)
        .map((action: discussion.RemoveAction) => action.payload)
        .withLatestFrom(this.store, (payload, state) => ({ discId: payload, state }))
        .mergeMap(({ discId, state }) => {
            // access the posts array of the discussion
            const postsToRemove = state.discussions[discId].posts;
    
            // Fire related Actions
            return [
                new posts.RemoveAction(postsToRemove)
            ];
        });
    

    The syntax .mergeMap(({ discId, state }) => ... is called destructuring.
    If you don't like this syntax, it can be replaced with .mergeMap((payloadAndState) => .... Then you would access discId by doing payloadAndState.discId