2

I've been struggling for 2 days to compose states of a featureA ("scenarios") module and a nested featureB ("intents") module.

Here is the desired structure of my state (structure 1):

{
  authentication: { ... },
  router: { ... },
  scenarios: {
    resources: { ids: { ... }, entities: { ... } },
    intents: {
      resources: { ids: { ... }, entities: { ... } }
    }
  }
}

I'd be happy also with (structure 2):

{
  authentication: { ... },
  router: { ... },
  scenarios: { ids: { ... }, entities: { ... } },
  intents: { ids: { ... }, entities: { ... } },
}

Although it doesn't reflect the structure of my modules.

The problem with structure 1 is that scenarios/reducers/index.ts exposes a ActionReducerMap, scenarios/modules/intents/reducers/index.ts also exposes a ActionReducerMap and I don't know how to compose them. Everything I tried doesn't even compile because of type conflicts.

The problem with structure 2 is that the intents part of the state keeps being dropped out of the state because of the IntentsModule not being loaded at first page load and other reducers of the app doing their job.

Here is the code:

/scenarios/scenarios.module.ts:

import { reducers, getInitialState } from './reducers';

@NgModule({
  imports: [
    ...,

    ScenariosRoutingModule,

    StoreModule.forFeature('scenarios', reducers, { initialState: getInitialState }),
    EffectsModule.forFeature([ScenariosEffects]),
  ],
})
export class ScenariosModule {}

/scenarios/reducers/index.ts:

import * as fromRoot from '../../../app.reducers';
import * as fromScenarios from './scenarios.reducers';
import * as fromIntents from '../modules/intents/reducers';

export interface ScenariosState {
  resources: fromScenarios.State;
  intents: fromIntents.IntentsState;
}

export interface State extends fromRoot.State {
  scenarios: ScenariosState;
}

export const reducers: ActionReducerMap<ScenariosState> = {
  resources: fromScenarios.reducer,
  intents: fromIntents.reducers, <-- This is the part with types conflicts
};

/scenarios/modules/intents/intents.module:

import { reducers, getInitialState } from './reducers';

@NgModule({
  imports: [
    ...,

    IntentsRoutingModule,

    StoreModule.forFeature('intents', reducers, { initialState: getInitialState }), <-- Also this part seems to add 'intents' as a top-level property of state
    EffectsModule.forFeature([IntentsEffects]),
  ],
})
export class IntentsModule {}

/scenarios/modules/intents/reducers/index.ts:

import * as fromIntents from './intents.reducers';

export interface IntentsState {
  resources: fromIntents.State;
}

export interface State {
  intents: IntentsState;
}

export const reducers: ActionReducerMap<IntentsState> = {
  resources: fromIntents.reducer,
};

export function getInitialState(): IntentsState {
  return {
    resources: fromIntents.initialState,
  };
}
paztek
  • 386
  • 3
  • 8

2 Answers2

2
  • import and export the structure 2 IntentsModule in the root AppModule.
  • import the ScenariosModule after the IntentsModule in the root AppModule.

Do not nest various IntentsModules under the ScenariosModules. Instead create separation and flatten your project structure as much as possible this is inherently required by structure 2.

I've found that a good approach for most situations with the exception of Auth related features is to create two modules per feature to be lazily loaded together yet decoupling state-management from the user-interface yet these corresponding modules should share an API of the same shape i.e:

  • (FrameworkLayoutModule and FrameworkStoreModule) and (AuthModule).

Subsequently, reusable features may be lazily loaded by a SecureModule or a PublicModule as needed.

girlvsdata
  • 1,596
  • 11
  • 21
  • My ScenariosModule is loaded in the AppRoutingModule with `loadChildren: './modules/scenarios/scenarios.module#ScenariosModule'`. My IntentsModule is loaded by the ScenariosRoutingModule with `loadChildren: './modules/intents/intents.module#IntentsModule'`. Can you point me to some good resources suggesting your approach with 2 modules per feature ? Thx. – paztek Aug 10 '18 at 08:01
0

This problem with use 'intents' store in two modules. And NgRx can`t create 'intents' store twice.

solution 1

remove 'intents' from scenarios.module or from intents.module if it dont use there.

solution 2

move 'intents' store into StoreModule.forRoot or into scenarios and intents parent module.

Kliment Ru
  • 2,057
  • 13
  • 16