To reduce the complexity of all the states in my app, I decided to use NGXS, because it's using the TypeScript way for implementation that fits erfectly with the Angular architecture. But the first problem appeared quite fast, because NGXS doesn't add an extra decoupling reducer layer compared to NGRX.
What is the best practise to interact between multiple state models? Suppose you want to manipulate state B, but a property of state A is required for this action. I found the shared state concept in the documentation that can handle this, but this is also limited, because I cannot use a shared state in a selector to provide the UI with a specific selection based on the action that state A and B require.
For example I got the following models that is represented in the store. The problem in this example, what is the best way to get the deviceId
of the selectedDevice
from the DeviceState
to use it in the DeviceHistoryState
to return all the item histories of the selected device.
Sure, I could integrate the DeviceHistory into the Device model, but this doesn't solve the problem of performing actions between multiple states. I also don't want to duplicate the selctedDevice into the DeviceHistoryStateModel.
export interface Device {
deviceId: string;
// More device details
}
export interface DeviceHistory {
deviceId: string;
itemHistoryMap: Map<number, ItemHistory[]>;
}
export class DeviceStateModel {
devices: Device[];
selectedDevice: Device;
}
@State<DeviceStateModel>({
name: 'devices',
defaults: {
devices: [],
selectedDevice: null
}
})
export class DeviceState {
}
export class DeviceHistoryStateModel {
devicesHistory: DeviceHistory[];
}
@State<DeviceHistoryStateModel>({
name: 'devicesHistory',
defaults: {
devicesHistory: []
}
})
export class DeviceHistoryState {
@Selector()
public static getHistory(state: DeviceHistoryStateModel) {
// ??? Best practise to return all the item histories of the selcted device
}
@Action(GetItemHistory)
public getItemHistory() {
// Stores the item history for the device
}
}
The simplest option would be to use a Joining Selector.
@Selector()
public static getHistory(state: DeviceHistoryStateModel, deviceState: DeviceStateModel) {
// ??? Best practise to return all the item histories of the selcted device
const selectedDevice = deviceState.selectedDevice;
//... get history items that match
}
The second option is likely to be what you are wanting, as you'd want this history selector to be re-evaluated automatically when the selectedDevice
value changed.
You might also want to check the version of NGXS you are running as there are recent (and upcoming changes) around the injected parameter options for selectors.
You could also make this work with a dynamic selector along these lines, passing in the device ID and getting filtered history records for that device back:
static getHistory(deviceId: string) {
return createSelector([DevicesHistoryState], (state: DevicesHistoryStateModel) => {
return state.devicesHistory.filter(h => h.deviceId === deviceId);
});
}