1
votes

I have an Angular app where I'm using ngrx. It's my first ngrx application, so I'm learning on the fly. I have actions, reducers, and effects set up to add items to a database and update those items, and that all works perfectly. The flow I have is that when they want to create a new item, they go to /item/new. When they've filled out the form, they go to /item/:id. I'm using the same component for adding/updating items, it just changes the URL to add the id if you're updating. Pretty basic stuff.

One of the things that I added to the ItemState is an array of notifications that can be added to and removed from while adding/udpdating items. When they leave the item detail page, either when updating or adding an item, I remove all the notifications from state. I chose to do this because otherwise when you go back to another detail page, or the list of items page, the notifications would be there. But I don't want that to happen. So in OnDestroy I dispatch that action. It works really well when updating an item to listen to the UpdateItemSuccess action to add a notification then because I don't navigate away from the /item/:id page after updating.

But after adding an item I then navigate to a new page by going from /item/add to /item/:id. So the notification, if added, is removed right away. So my thought was that on the redirect I could add something to the URL, like #itemAdded=true or ?itemAdded=true that I could grab, add a notification, and remove from the URL. So I added an effect for the routerStore.Go action to look at the payload to check for that information, but couldn't figure out how to check that and then allow it to continue on with its routing. It also must have done something else because the page would become unresponsive and I had to quit Chrome and restart it. :)

I know another way to do this is to subscribe to the Router params in the component and add the notification that way. I could still dispatch the create notification through the store, but there would be that logic in the component, where all other similar logic is in the store. So that doesn't seem like the correct way to do this. Is there any other way that I can achieve this functionality?

1

1 Answers

1
votes

Not sure if I understood you correctly but I think that kind of logic could be better handled at a selector level. If we assume having a notification array in a given state:

export interface AppState {
    ...
    notifications: Notification[];
}

Then I'd treat notifications as entities and try to link them to their respective items with maybe an extra information about if they where read or not in case I want to dispatch an action somewhere to disable them:

export interface Notification {
    itemId?: number;
    message?: string;
    read?: boolean;
}

Then instead of making my component listen to a getAllNotifications selector, I'd create a sub-selector to it like the following:

export const getSelectedNotifications = createSelector(
  getAllNotifications,
  fromRoot.getRouterState,
  (notifications, router): Notification[] => {
    return 
        router.state && router.state.params.id 
            ? notifications.filter(
                notif => 
                    notif.itemId === router.state.params.id 
                    && notif.read === false
                ) 
            : [];
  }
);

By subscribing to it, my component will only know about the unread notifications that matches the item id present in the router url /item/:id