问题
Following is my angular app structure
app.module.ts
-- StoreModule.forRoot()
mainboard.module.ts
--StoreModule.forFeature('mainboard', reducer)
subFeature1.module.ts
subFeature2.module.ts
subFeature3.module.ts
dashboard1.module.ts
--StoreModule.forFeature('dashboard1', reducer)
subFeature1.module.ts
subFeature2.module.ts
subFeature3.module.ts
subFeature4.module.ts
subFeature5.module.ts
dashboard2.module.ts
--StoreModule.forFeature('dashboard2', reducer)
subFeature1.module.ts
subFeature2.module.ts
dashboard3.module.ts
--StoreModule.forFeature('dashboard3', reducer)
subFeature1.module.ts
subFeature2.module.ts
subFeature3.module.ts
subFeature4.module.ts
dashboard4.module.ts
--StoreModule.forFeature('dashboard4', reducer)
subFeature1.module.ts
subFeature2.module.ts
So the requirement is to have store for subfeatures.
Like this:
app.module.ts
-- StoreModule.forRoot()
mainboard.module.ts
--StoreModule.forFeature('mainboard', reducer)
subFeature1.module.ts
--StoreModule.forFeature('subFeature1', reducer)
subFeature2.module.ts
--StoreModule.forFeature('subFeature1', reducer)
subFeature3.module.ts
--StoreModule.forFeature('subFeature1', reducer)
...
HOW CAN I ACHIEIVE SUCH A HIERARCHY FOR MY NGRX/STORE ?
app.module is there all the dashboards are placed.
dashboard-x.modules are the places where all the navs/ items are placed.
and inside each dashboards sub features are included.
So my question is about how to register subFeatures with StoreModule.forFeature()?
Or do I need to make stores for each dashboards (StoreModule.forRoot()) and then StoreModule.forFeature() for each subFeatures? (if so then how may I be able to register it inside the app's StoreModule.forRoot())
NOTE
Currently I'm registering all of the subfeatures as forFeature.('subfeturename', reducer)
But the problem with this is that when I have a look at my state tree (Redux devtools) it is not following the above store tree structure. Since all the sub features are getting registered as forFeature() all of them are showing as properties (ie, they are not getting nested as expected). What I want instead is that I want to have them nested inside my state tree.
So if I have a look at my state tree I can see nested state Like this:
app
mainboard(open)
subFeature1
subFeature2
subFeature3
dashboard1(closed)
dashboard2(open)
subFeature1
subFeature2
dashboard3(closed)
dashboard4(closed)
//open and closed means expand and collapsed tree
Remember, each level (app > dashboard1 > subfeature) has different properties which needs to be managed by stores. So a store is necessary for each level.
回答1:
When you're registering a feature, you have 2 options:
StoreModule.forFeature('featName', reducer)
or
StoreModule.forFeature('featName', { subFeat1: subFeat1Reducer, subFeat2: subFeat2Reducer })
So I think you can achieve what you're looking for by using the second approach.
回答2:
so this can be a little confusing since its a bit black boxed but essentially in each of you modules that you lazy load in you will add the forfeature method just as you are doing.
Core or Root AppState:
export interface AppState {
layout: LayoutState;
...etc
}
export const appReducer: ActionReducerMap<AppState> = {
layout: layoutReducer,
...etc
};
add this to imports in your core.module or app.module
StoreModule.forRoot(appReducer),
a lazy loaded or othe rimported module will have a completely decoupled store that gets appended to this main appstate when the module is loaded
export interface FeatureAppState {
featureSettings: FeatureState;
...etc
};
export const FeatureAppReducer: ActionReducerMap<FeatureAppState> = {
featureSettings: FeatureReducer,
...etc
};
and in your module for that feature you import the following
StoreModule.forFeature('featureApp', FeatureAppReducer)
AND THATS IT! it won't be hierarchical in nature though you will want to use selectors in your stores so for example to get layout in the parent state
export const selectLayoutState = createSelector((state: AppState) => state, (state: AppState) => state.layout);
export const selectLayoutMainMenu = createSelector(selectLayoutState, state => state.mainMenu);
would get you the mainmenu from the layout store from core and in your feature stores to get the feature state
export const getFeatureState =
createSelector(createFeatureSelector('featureApp'), (state: FeatureAppState)
=> state.featureSettings);
remember that angular works off the concept of reusability so things in stat tend to be very flat so you could import subfeature1 under mainboard and dashboard2 and they will utilize the same store.
If you need to manage states tied to different parents you may try using something like ngrx entity the idea being you could store each state with a unique id for that parent feature or pass a string into the forFeature in your feature module so for example using forChild on the import you could pass in an argument and pass the argument into the StoreModules.forFeature import . I'm not sure that would work but you could try it. In the end I think this is a bad design though and there are better ways of doing it.
but your not going to get a new state using forFeature for each time you import the module thats just not how it works.
回答3:
There are a lot of way to do it. Below will be only my option based on my experience.
First thing to note -
forFeaturedoesn't create an independent store context. It simply connects new reducers and effects after call offorRootand these effects and reducers will get all actions from the whole application, so if you sendupdateMyLazyFeature(123)in one module - all modules that connected its reducer viaforFeaturewill get this action and reducer it, and it's very easy to get data collisions, when you think that only your module will be updated but it affects other parts of the application.
When you want to split your store on independent features you need to review how a store feature is used. If a feature is used in more than 1 module, it will be located in shared file after build and doesn't have any benifit compare to forRoot.
if the feature is used in several modules - the best place would be to keep it in forRoot.
If you use a feature only in a particular module and it's so tied that there's zero chance to use it anywhere else - then you can put it as a separate store feature.
One more case - if you implement a library with ngrx store, then you need to use
forFeaturebecause you can't accessforRootfrom your lib.
An example, we have a dashboard application and users have to login. It means that we should have access to user's data in several components and then the right place would be to put the feature in forRoot.
Apart from that, we have a lazy registration module and a user can use an invitation link to prefill the form. Because the data won't be used anywhere else and the registration module is seldomly used too, we can implement the feature as forFeature in the registration module. If user isn't going to register during his session - angular won't be going to load the module and the feature at all.
Also flux pattern it's important to keep data flat. The main problem flux and ngrx solve is data consistency and the easiest way to achieve it is to keep it flat.
Despite modules are nested like app -> mainboard -> subFeature1 etc, you still can add them to the top level of the store so they'll be stored on the same level as other reducers.
store
users
mainboard
dashboard2
subFeature1
subFeature2
subFeature3
There's no need to make it like
store
users
mainboard
subFeature1
subFeature2
subFeature3
dashboard2
subFeature1
subFeature2
subFeature3
It will bring complexity later when behavior of selectors, reducers and actions isn't so clear and predictable. With the flat structure you can move your feature to another project with ease.
Based on this info I would vote for flat structure and would use
StoreModule.forFeature('logsControls', logsControls.reducer);
instead of
StoreModule.forFeature('dashboard1', {
logsControls: logsControls.reducer,
});
StoreModule.forFeature('dashboard2', {
logsControls: logsControls.reducer,
});
Because in the second case an action will update both dashboard1 and dashboard2 and requires a technique like correlationId to affect only one feature.
来源:https://stackoverflow.com/questions/61888783/how-to-register-ngrx-sub-features-storemodule-forfeature-for-my-angular-app