Angular CLI schematics for generating NgRx state management code including actions, reducers, effects, selectors, and feature modules.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
NgRx effect generation schematic that creates side effect classes for handling asynchronous operations and external interactions in NgRx applications. Effects listen for actions, perform side effects like HTTP requests, and dispatch new actions based on the results.
Generates NgRx effect classes using createEffect() with proper action filtering and error handling.
# Basic effect generation
ng generate @ngrx/schematics:effect User
# Effect with creator functions
ng generate @ngrx/schematics:effect User --creators
# Root-level effect
ng generate @ngrx/schematics:effect User --root/**
* Effect schematic configuration interface
*/
interface EffectSchema {
/** Name of the effect class (typically entity or feature name) */
name: string;
/** Path where effect files should be generated */
path?: string;
/** Angular project to target */
project?: string;
/** Generate files without creating a folder */
flat?: boolean;
/** Group effect files within folders */
group?: boolean;
/** Module file to register effects in */
module?: string;
/** Generate as feature effect */
feature?: boolean;
/** Generate as root effect */
root?: boolean;
/** Generate minimal effect setup */
minimal?: boolean;
/** Use creator functions */
creators?: boolean;
}Creates effect classes with common patterns for API calls and side effect management.
// Generated effect class
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
import * as UserActions from './user.actions';
import { UserService } from './user.service';
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({ error })))
)
)
)
);
createUser$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.createUser),
switchMap(({ user }) =>
this.userService.createUser(user).pipe(
map(createdUser => UserActions.createUserSuccess({ user: createdUser })),
catchError(error => of(UserActions.createUserFailure({ error })))
)
)
)
);
updateUser$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.updateUser),
switchMap(({ user }) =>
this.userService.updateUser(user).pipe(
map(updatedUser => UserActions.updateUserSuccess({ user: updatedUser })),
catchError(error => of(UserActions.updateUserFailure({ error })))
)
)
)
);
deleteUser$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.deleteUser),
switchMap(({ id }) =>
this.userService.deleteUser(id).pipe(
map(() => UserActions.deleteUserSuccess({ id })),
catchError(error => of(UserActions.deleteUserFailure({ error })))
)
)
)
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}Usage Examples:
# Generate user effects
ng generate @ngrx/schematics:effect User --creators
# Generate product effects with custom path
ng generate @ngrx/schematics:effect Product --path=src/app/catalog --creators
# Generate root-level auth effects
ng generate @ngrx/schematics:effect Auth --root --module=app.module.tsEffects that perform side effects without dispatching actions.
// Non-dispatching effect example
@Injectable()
export class LoggingEffects {
logActions$ = createEffect(() =>
this.actions$.pipe(
tap(action => console.log('Action dispatched:', action))
),
{ dispatch: false }
);
saveToLocalStorage$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.updateUserPreferences),
tap(({ preferences }) => {
localStorage.setItem('userPreferences', JSON.stringify(preferences));
})
),
{ dispatch: false }
);
constructor(private actions$: Actions) {}
}Comprehensive error handling strategies in generated effects.
/**
* Error handling patterns for effects
*/
interface ErrorHandlingPatterns {
/** Basic catch and dispatch error action */
basicCatch: 'catchError(error => of(ErrorAction({ error })))';
/** Retry with exponential backoff */
retryWithBackoff: 'retryWhen(errors => errors.pipe(delay(1000), take(3)))';
/** Handle specific error types */
specificErrors: 'catchError(error => error.status === 404 ? of(NotFoundAction()) : of(ErrorAction({ error })))';
}// Advanced error handling
loadUsersWithRetry$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
retry(3),
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => {
// Log error for monitoring
console.error('Failed to load users:', error);
// Handle different error types
if (error.status === 404) {
return of(UserActions.noUsersFound());
} else if (error.status === 403) {
return of(UserActions.unauthorizedAccess());
} else {
return of(UserActions.loadUsersFailure({
error: error.message || 'Unknown error occurred'
}));
}
})
)
)
)
);Effects that interact with Angular Router for navigation.
// Router integration effects
import { Router } from '@angular/router';
@Injectable()
export class RouterEffects {
navigateToUser$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.selectUser),
tap(({ userId }) => {
this.router.navigate(['/users', userId]);
})
),
{ dispatch: false }
);
redirectAfterLogin$ = createEffect(() =>
this.actions$.pipe(
ofType(AuthActions.loginSuccess),
tap(() => {
this.router.navigate(['/dashboard']);
})
),
{ dispatch: false }
);
constructor(
private actions$: Actions,
private router: Router
) {}
}Automatic registration of effects in Angular modules.
// Generated module registration
import { EffectsModule } from '@ngrx/effects';
import { UserEffects } from './user.effects';
@NgModule({
imports: [
EffectsModule.forFeature([UserEffects])
]
})
export class UserModule {}
// Root module registration
@NgModule({
imports: [
EffectsModule.forRoot([AppEffects])
]
})
export class AppModule {}Generated effects include comprehensive testing utilities.
// Effect testing example
describe('UserEffects', () => {
let actions$: Observable<any>;
let effects: UserEffects;
let service: jasmine.SpyObj<UserService>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
UserEffects,
provideMockActions(() => actions$),
{
provide: UserService,
useValue: jasmine.createSpyObj('UserService', ['getUsers'])
}
]
});
effects = TestBed.inject(UserEffects);
service = TestBed.inject(UserService) as jasmine.SpyObj<UserService>;
});
describe('loadUsers$', () => {
it('should return loadUsersSuccess on successful load', () => {
const users = [{ id: '1', name: 'John' }];
const action = UserActions.loadUsers();
const outcome = UserActions.loadUsersSuccess({ users });
actions$ = hot('-a', { a: action });
const response = cold('-a|', { a: users });
service.getUsers.and.returnValue(response);
const expected = cold('--b', { b: outcome });
expect(effects.loadUsers$).toBeObservable(expected);
});
});
});Generated effects include performance best practices.
/**
* Performance optimization patterns in effects
*/
interface PerformancePatterns {
/** Use switchMap for cancellable operations */
switchMap: 'switchMap(() => service.call())';
/** Use mergeMap for parallel operations */
mergeMap: 'mergeMap(() => service.call())';
/** Use concatMap for sequential operations */
concatMap: 'concatMap(() => service.call())';
/** Use exhaustMap to ignore new actions while processing */
exhaustMap: 'exhaustMap(() => service.call())';
}// Optimized effect patterns
@Injectable()
export class OptimizedEffects {
// Cancel previous requests when new search is triggered
searchUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.searchUsers),
debounceTime(300), // Debounce user input
distinctUntilChanged(
(prev, curr) => prev.query === curr.query
),
switchMap(({ query }) =>
this.userService.searchUsers(query).pipe(
map(users => UserActions.searchUsersSuccess({ users })),
catchError(error => of(UserActions.searchUsersFailure({ error })))
)
)
)
);
// Process operations in parallel
loadMultipleResources$ = createEffect(() =>
this.actions$.pipe(
ofType(AppActions.loadDashboard),
mergeMap(() => [
UserActions.loadUsers(),
ProductActions.loadProducts(),
OrderActions.loadOrders()
])
)
);
// Prevent multiple login attempts
login$ = createEffect(() =>
this.actions$.pipe(
ofType(AuthActions.login),
exhaustMap(({ credentials }) =>
this.authService.login(credentials).pipe(
map(user => AuthActions.loginSuccess({ user })),
catchError(error => of(AuthActions.loginFailure({ error })))
)
)
)
);
}Effects for real-time communication with WebSocket integration.
// WebSocket effect example
@Injectable()
export class WebSocketEffects {
connectToWebSocket$ = createEffect(() =>
this.actions$.pipe(
ofType(AppActions.initializeApp),
switchMap(() =>
this.webSocketService.connect().pipe(
map(message => WebSocketActions.messageReceived({ message })),
catchError(error => of(WebSocketActions.connectionError({ error })))
)
)
)
);
sendMessage$ = createEffect(() =>
this.actions$.pipe(
ofType(WebSocketActions.sendMessage),
tap(({ message }) => {
this.webSocketService.send(message);
})
),
{ dispatch: false }
);
constructor(
private actions$: Actions,
private webSocketService: WebSocketService
) {}
}