Side effect model for @ngrx/store that manages asynchronous operations and external interactions in Angular applications using RxJS
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Integration capabilities for NgRx Effects with Angular's module system and standalone applications, providing flexible setup options for different application architectures.
NgModule for setting up effects in module-based Angular applications with support for root and feature-level configuration.
/**
* NgModule for integrating effects into module-based Angular applications
*/
class EffectsModule {
/**
* Configure root-level effects for the application
* @param effects - Array of effect classes to register at root level
* @returns ModuleWithProviders for root effects configuration
*/
static forRoot(effects: Type<any>[]): ModuleWithProviders<EffectsRootModule>;
/**
* Configure feature-level effects for lazy-loaded modules
* @param effects - Array of effect classes to register at feature level
* @returns ModuleWithProviders for feature effects configuration
*/
static forFeature(effects: Type<any>[]): ModuleWithProviders<EffectsFeatureModule>;
}
class EffectsRootModule {}
class EffectsFeatureModule {}Usage Examples:
import { NgModule } from "@angular/core";
import { EffectsModule } from "@ngrx/effects";
import { StoreModule } from "@ngrx/store";
// Root module setup
@NgModule({
imports: [
StoreModule.forRoot({}),
EffectsModule.forRoot([
AppEffects,
RouterEffects,
AuthEffects
])
],
// ...
})
export class AppModule {}
// Feature module setup
@NgModule({
imports: [
StoreModule.forFeature('books', bookReducer),
EffectsModule.forFeature([
BookEffects,
BookSearchEffects
])
],
// ...
})
export class BooksModule {}
// Lazy-loaded feature module
@NgModule({
imports: [
RouterModule.forChild(routes),
StoreModule.forFeature('admin', adminReducer),
EffectsModule.forFeature([AdminEffects])
],
// ...
})
export class AdminModule {}Provider function for registering effects in standalone Angular applications and with the new provider-based configuration.
/**
* Provides effects for standalone applications or provider-based configuration
* @param effects - Array of effect classes or effect creation functions
* @returns EnvironmentProviders for effects registration
*/
function provideEffects(...effects: Array<Type<unknown> | Record<string, FunctionalEffect>>): EnvironmentProviders;
type FunctionalEffect<Source extends () => Observable<unknown> = () => Observable<unknown>> =
Source & CreateEffectMetadata;Usage Examples:
import { bootstrapApplication } from "@angular/platform-browser";
import { provideStore } from "@ngrx/store";
import { provideEffects } from "@ngrx/effects";
// Standalone application bootstrap
bootstrapApplication(AppComponent, {
providers: [
// Store setup
provideStore({}),
// Effects setup with classes
provideEffects(
AppEffects,
UserEffects,
NotificationEffects
),
// Mix of classes and functional effects
provideEffects(
AuthEffects, // class
loadUserEffect, // functional effect
syncDataEffect // functional effect
)
]
});
// Feature route configuration
export const ADMIN_ROUTES: Route[] = [
{
path: '',
providers: [
provideStore(adminFeature),
provideEffects(AdminEffects, AuditEffects)
],
children: [
// ... child routes
]
}
];
// Testing setup with provideEffects
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideStore(testReducer),
provideEffects(TestEffects),
provideMockActions(() => actions$)
]
});
});Utility function for combining multiple effects into a single observable stream, useful for complex effect compositions.
/**
* Merges multiple effects into a single observable stream
* @param effects - Array of observables or functions returning observables
* @returns Function returning merged observable of actions
*/
function mergeEffects(...effects: Array<Observable<Action> | (() => Observable<Action>)>): () => Observable<Action>;Usage Examples:
import { mergeEffects } from "@ngrx/effects";
// Combine multiple effect streams
const combinedUserEffects = mergeEffects(
// Observable streams
userLoadEffect$,
userUpdateEffect$,
// Effect functions
() => userDeleteEffect$,
() => userValidationEffect$
);
// Use in provider setup
bootstrapApplication(AppComponent, {
providers: [
provideStore({}),
provideEffects(
AuthEffects,
combinedUserEffects, // Merged effects as single provider
NotificationEffects
)
]
});
// Complex merging with conditional logic
const dynamicEffects = mergeEffects(
baseEffects$,
featureFlag.enabled ? advancedEffects$ : empty(),
development ? debugEffects$ : empty()
);
// Testing merged effects
const testEffects = mergeEffects(
mockEffect1$,
mockEffect2$,
() => mockEffect3$
);Internal services for managing effect sources and lifecycle.
/**
* Service managing effect source instances and conversion to action streams
*/
class EffectSources extends Subject<any> {
/**
* Add effect instances to the sources
* @param effectSourceInstance - Effect class instance to add
*/
addEffects(effectSourceInstance: any): void;
/**
* Convert effect sources to a stream of actions
* @returns Observable stream of all effect-generated actions
*/
toActions(): Observable<Action>;
}
/**
* Service responsible for running and managing effect lifecycle
*/
class EffectsRunner {
/**
* Start the effects runner and begin processing effects
*/
start(): void;
}Conditional Effects Loading:
// Load effects based on feature flags
const effectsToLoad = [
CoreEffects,
...(featureFlags.newUserFlow ? [NewUserEffects] : []),
...(featureFlags.analytics ? [AnalyticsEffects] : [])
];
bootstrapApplication(AppComponent, {
providers: [
provideStore({}),
provideEffects(...effectsToLoad)
]
});Dynamic Effect Registration:
// Service for dynamically registering effects
@Injectable()
export class DynamicEffectsService {
constructor(private effectSources: EffectSources) {}
registerEffects(effectClass: Type<any>) {
const effectInstance = this.injector.get(effectClass);
this.effectSources.addEffects(effectInstance);
}
}Testing with Multiple Effect Sources:
describe('Feature Effects', () => {
let actions$: Observable<any>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideStore(testReducer),
provideEffects(
FeatureEffects,
SharedEffects
),
provideMockActions(() => actions$)
]
});
});
});/** Token for user-provided effects arrays */
const USER_PROVIDED_EFFECTS: InjectionToken<any[][]>;
/** Internal token for root-level effects */
const _ROOT_EFFECTS: InjectionToken<any[]>;
/** Internal token for feature-level effects */
const _FEATURE_EFFECTS: InjectionToken<any[]>;