CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ngrx--store

RxJS powered Redux state management for Angular applications

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

module-configuration.mddocs/

Module Configuration

Module configuration provides NgModule-based setup for traditional Angular applications using the StoreModule with forRoot and forFeature patterns for root and feature state management.

Capabilities

Store Module

Main NgModule for configuring NgRx Store in module-based Angular applications.

/**
 * NgModule for configuring NgRx Store
 */
class StoreModule {
  /**
   * Configure the root store with initial reducers and configuration
   * @param reducers - Map of reducers or injection token for reducers
   * @param config - Root store configuration options
   * @returns ModuleWithProviders for StoreRootModule
   */
  static forRoot<T, V extends Action = Action>(
    reducers?: ActionReducerMap<T, V> | InjectionToken<ActionReducerMap<T, V>>,
    config?: RootStoreConfig<T, V>
  ): ModuleWithProviders<StoreRootModule>;
  
  /**
   * Configure feature store with feature-specific reducers
   * @param featureName - Name/key for the feature state slice
   * @param reducers - Feature reducers map or single reducer
   * @param config - Feature store configuration options
   * @returns ModuleWithProviders for StoreFeatureModule
   */
  static forFeature<T, V extends Action = Action>(
    featureName: string,
    reducers: ActionReducerMap<T, V> | InjectionToken<ActionReducerMap<T, V>>,
    config?: StoreConfig<T, V> | InjectionToken<StoreConfig<T, V>>
  ): ModuleWithProviders<StoreFeatureModule>;
  
  static forFeature<T, V extends Action = Action>(
    featureName: string,
    reducer: ActionReducer<T, V> | InjectionToken<ActionReducer<T, V>>,
    config?: StoreConfig<T, V> | InjectionToken<StoreConfig<T, V>>
  ): ModuleWithProviders<StoreFeatureModule>;
  
  static forFeature<T, V extends Action = Action>(
    slice: FeatureSlice<T, V>
  ): ModuleWithProviders<StoreFeatureModule>;
}

/**
 * Root store module implementation
 */
class StoreRootModule {}

/**
 * Feature store module implementation
 */
class StoreFeatureModule implements OnDestroy {}

Usage Examples:

import { NgModule } from "@angular/core";
import { StoreModule } from "@ngrx/store";
import { reducers, metaReducers } from "./app.reducers";

// Root module configuration
@NgModule({
  imports: [
    StoreModule.forRoot(reducers, {
      metaReducers,
      runtimeChecks: {
        strictStateImmutability: true,
        strictActionImmutability: true,
        strictStateSerializability: true,
        strictActionSerializability: true,
        strictActionWithinNgZone: true,
        strictActionTypeUniqueness: true
      }
    })
  ]
})
export class AppModule {}

// Feature module with reducer map
@NgModule({
  imports: [
    StoreModule.forFeature('users', {
      list: userListReducer,
      profile: userProfileReducer,
      preferences: userPreferencesReducer
    }, {
      metaReducers: [userMetaReducer],
      initialState: {
        list: { entities: [], loading: false },
        profile: null,
        preferences: defaultPreferences
      }
    })
  ]
})
export class UserModule {}

// Feature module with single reducer
@NgModule({
  imports: [
    StoreModule.forFeature('orders', orderReducer, {
      initialState: initialOrderState
    })
  ]
})
export class OrderModule {}

// Feature module with FeatureSlice
@NgModule({
  imports: [
    StoreModule.forFeature({
      name: 'products',
      reducer: productReducer,
      initialState: initialProductState
    })
  ]
})
export class ProductModule {}

Configuration Types

Root Store Configuration

/**
 * Configuration options for root store
 */
interface RootStoreConfig<T, V extends Action = Action> {
  /** Initial state for the entire application */
  initialState?: InitialState<T>;
  /** Meta-reducers applied to all reducers */
  metaReducers?: MetaReducer<T, V>[];
  /** Runtime validation checks configuration */
  runtimeChecks?: Partial<RuntimeChecks>;
}

/**
 * Runtime validation checks configuration
 */
interface RuntimeChecks {
  /** Verifies if the state is serializable */
  strictStateSerializability: boolean;
  /** Verifies if the actions are serializable */
  strictActionSerializability: boolean;
  /** Verifies that the state isn't mutated */
  strictStateImmutability: boolean;
  /** Verifies that actions aren't mutated */
  strictActionImmutability: boolean;
  /** Verifies that actions are dispatched within NgZone */
  strictActionWithinNgZone: boolean;
  /** Verifies that action types are not registered more than once */
  strictActionTypeUniqueness?: boolean;
}

Feature Store Configuration

/**
 * Configuration options for feature store
 */
interface StoreConfig<T, V extends Action = Action> {
  /** Initial state for the feature */
  initialState?: InitialState<T>;
  /** Meta-reducers applied to feature reducers */
  metaReducers?: MetaReducer<T, V>[];
}

/**
 * Feature slice configuration
 */
interface FeatureSlice<T, V extends Action = Action> {
  /** Name/key for the feature */
  name: string;
  /** Reducer function for the feature */
  reducer: ActionReducer<T, V>;
  /** Initial state for the feature */
  initialState?: InitialState<T>;
  /** Meta-reducers for the feature */
  metaReducers?: MetaReducer<T, V>[];
}

/**
 * Initial state configuration type
 */
type InitialState<T> = Partial<T> | TypeId<Partial<T>> | void;
type TypeId<T> = () => T;

Advanced Configuration Patterns

Lazy-Loaded Feature Modules

// Lazy-loaded feature module
@NgModule({
  imports: [
    CommonModule,
    StoreModule.forFeature('billing', billingReducer, {
      initialState: () => ({
        // Dynamic initial state
        invoices: [],
        currentPlan: getCurrentUserPlan(),
        settings: getUserBillingSettings()
      })
    })
  ]
})
export class BillingModule {}

// Route configuration with lazy loading
const routes: Routes = [
  {
    path: 'billing',
    loadChildren: () => import('./billing/billing.module').then(m => m.BillingModule)
  }
];

Injectable Configuration

import { InjectionToken } from "@angular/core";

// Injectable reducer configuration
export const USER_REDUCERS = new InjectionToken<ActionReducerMap<UserState>>('User Reducers', {
  factory: () => ({
    profile: userProfileReducer,
    settings: userSettingsReducer,
    notifications: userNotificationsReducer
  })
});

export const USER_CONFIG = new InjectionToken<StoreConfig<UserState>>('User Config', {
  factory: () => ({
    initialState: {
      profile: null,
      settings: defaultUserSettings,
      notifications: { enabled: true, preferences: {} }
    },
    metaReducers: environment.production ? [] : [storeFreeze]
  })
});

// Module using injectable tokens
@NgModule({
  imports: [
    StoreModule.forFeature('users', USER_REDUCERS, USER_CONFIG)
  ],
  providers: [
    // Custom providers if needed
    { provide: USER_CONFIG, useFactory: createUserConfig, deps: [UserService] }
  ]
})
export class UserModule {}

Meta-Reducers Configuration

import { MetaReducer } from "@ngrx/store";
import { storeFreeze } from "ngrx-store-freeze";
import { storeLogger } from "./store-logger";

// Environment-specific meta-reducers
export const metaReducers: MetaReducer<AppState>[] = !environment.production 
  ? [storeLogger, storeFreeze] 
  : [];

// Feature-specific meta-reducers
const userMetaReducers: MetaReducer<UserState>[] = [
  // Audit trail for user actions
  auditTrailMetaReducer,
  // Validation for user state changes
  userValidationMetaReducer
];

@NgModule({
  imports: [
    StoreModule.forRoot(reducers, { metaReducers }),
    StoreModule.forFeature('users', userReducer, { 
      metaReducers: userMetaReducers 
    })
  ]
})
export class AppModule {}

Multi-Environment Configuration

// Environment-specific configurations
const developmentConfig: RootStoreConfig<AppState> = {
  runtimeChecks: {
    strictStateImmutability: true,
    strictActionImmutability: true,
    strictStateSerializability: true,
    strictActionSerializability: true,
    strictActionWithinNgZone: true,
    strictActionTypeUniqueness: true
  },
  metaReducers: [storeLogger, storeFreeze]
};

const productionConfig: RootStoreConfig<AppState> = {
  runtimeChecks: {
    strictStateImmutability: false,
    strictActionImmutability: false,
    strictStateSerializability: false,
    strictActionSerializability: false,
    strictActionWithinNgZone: false,
    strictActionTypeUniqueness: false
  },
  metaReducers: []
};

@NgModule({
  imports: [
    StoreModule.forRoot(
      reducers, 
      environment.production ? productionConfig : developmentConfig
    )
  ]
})
export class AppModule {}

Module Lifecycle Integration

Feature Module Lifecycle

import { OnDestroy } from "@angular/core";
import { Store } from "@ngrx/store";

@NgModule({
  imports: [
    StoreModule.forFeature('analytics', analyticsReducer)
  ]
})
export class AnalyticsModule implements OnDestroy {
  constructor(private store: Store) {
    // Initialize feature on module load
    this.store.dispatch(initializeAnalytics());
  }
  
  ngOnDestroy() {
    // Cleanup when module is destroyed
    this.store.dispatch(cleanupAnalytics());
  }
}

Dynamic Feature Registration

import { Injector } from "@angular/core";
import { Store } from "@ngrx/store";

export class DynamicFeatureService {
  constructor(
    private store: Store,
    private injector: Injector
  ) {}
  
  loadFeature(featureName: string, reducer: ActionReducer<any>) {
    // Dynamically add feature reducer
    this.store.addReducer(featureName, reducer);
    
    // Initialize feature state
    this.store.dispatch(initializeFeature({ featureName }));
  }
  
  unloadFeature(featureName: string) {
    // Cleanup feature state
    this.store.dispatch(cleanupFeature({ featureName }));
    
    // Remove feature reducer
    this.store.removeReducer(featureName);
  }
}

Integration with Angular Router

import { Router } from "@angular/router";
import { Store } from "@ngrx/store";

// Route-based feature loading
const routes: Routes = [
  {
    path: 'dashboard',
    loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule),
    resolve: {
      // Preload dashboard data
      dashboardData: (route, state) => {
        const store = inject(Store);
        store.dispatch(loadDashboardData());
        return store.select(selectDashboardLoaded).pipe(
          filter(loaded => loaded),
          take(1)
        );
      }
    }
  }
];

Best Practices

  1. Root Configuration: Configure runtime checks and meta-reducers at the root level
  2. Feature Isolation: Use separate feature modules for different business domains
  3. Lazy Loading: Combine with Angular's lazy loading for optimal bundle sizes
  4. Environment Configuration: Use different configurations for development and production
  5. Injectable Tokens: Use injection tokens for complex configuration scenarios
  6. Meta-Reducers: Apply feature-specific meta-reducers for targeted functionality

docs

action-creators.md

feature-management.md

index.md

module-configuration.md

reducer-creators.md

selectors.md

standalone-providers.md

store-service.md

testing-utilities.md

tile.json