My Angular application shows a list of Item
where every Item
(Component) has its own state composed by the following properties:
{
name: 'item name',
status: 'OK'
}
I used a ngrx store to mantain an updated state for every component (the OK state is coming from a backend-service via Effect, so I retrieve data asynchronously to be put into the store).
Angular polls a backend-service and if the status field for the n-th item is not OK, the UI has to be colored differently.
The back-end service returns an array of items of the type shown above and it is placed into the list
below.
So, here's the State representation:
export interface State {
list: Item[]
}
Now I would like to store one more information in every Item of the list upon an event: the open
property which is not depending by the backend. It is set via UI upon a button click.
So I would have a state like this at a certain point:
[{
name: 'item 1',
status: 'OK',
open: true
},
{
name: 'item 2',
status: 'OK',
open: false
},
{
name: 'item 3',
status: 'OK'
}
...
{
name: 'item n',
status: null
}]
The open
property is set by a UI event. Other properties come from the backend (via array).
My problem is that every time I dispatch an action that pulls data from the back-end service, all the elements are overwritten (I clearly understand why) and I have this:
[{
name: 'item 1',
status: 'OK'
},
{
name: 'item 2',
status: 'OK'
},
{
name: 'item 3',
status: 'OK'
}
...
{
name: 'item n',
status: null
}]
What's the best approach to solve this? I think it could be easily solved into the reducer, but I still can't figure out how.
I tried to:
but I think I will have synch issues in both cases
case ItemsActions.STORE_ITEM_LIST:
return {
...state,
list: action.payload
}
to this (update each item specifying each field):
case ItemsActions.STORE_ITEM_LIST:
return {
...state,
list: action.payload.map((item, index) => {
action.payload[index].name === item.name ? {
...item,
name: item.name,
status: item.status
} : item
})
}
In theory you have to map over the payload and "join" the current state value with the incoming value.
This translates into the following implementation:
case ItemsActions.STORE_ITEM_LIST:
return {
...state,
list: action.payload.map((item) => ({
...state.list.find(item2 => item2.name === item.name) || {}, // if the item isn't found just create an empty object
name: item.name,
status: item.status
}))
}
To make these updates easier you can take a look at: