0
# Effect Generation
1
2
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.
3
4
## Capabilities
5
6
### Effect Schematic
7
8
Generates NgRx effect classes using createEffect() with proper action filtering and error handling.
9
10
```bash
11
# Basic effect generation
12
ng generate @ngrx/schematics:effect User
13
14
# Effect with creator functions
15
ng generate @ngrx/schematics:effect User --creators
16
17
# Root-level effect
18
ng generate @ngrx/schematics:effect User --root
19
```
20
21
```typescript { .api }
22
/**
23
* Effect schematic configuration interface
24
*/
25
interface EffectSchema {
26
/** Name of the effect class (typically entity or feature name) */
27
name: string;
28
/** Path where effect files should be generated */
29
path?: string;
30
/** Angular project to target */
31
project?: string;
32
/** Generate files without creating a folder */
33
flat?: boolean;
34
/** Group effect files within folders */
35
group?: boolean;
36
/** Module file to register effects in */
37
module?: string;
38
/** Generate as feature effect */
39
feature?: boolean;
40
/** Generate as root effect */
41
root?: boolean;
42
/** Generate minimal effect setup */
43
minimal?: boolean;
44
/** Use creator functions */
45
creators?: boolean;
46
}
47
```
48
49
### Basic Effect Generation
50
51
Creates effect classes with common patterns for API calls and side effect management.
52
53
```typescript
54
// Generated effect class
55
import { Injectable } from '@angular/core';
56
import { Actions, createEffect, ofType } from '@ngrx/effects';
57
import { catchError, map, switchMap } from 'rxjs/operators';
58
import { of } from 'rxjs';
59
import * as UserActions from './user.actions';
60
import { UserService } from './user.service';
61
62
@Injectable()
63
export class UserEffects {
64
65
loadUsers$ = createEffect(() =>
66
this.actions$.pipe(
67
ofType(UserActions.loadUsers),
68
switchMap(() =>
69
this.userService.getUsers().pipe(
70
map(users => UserActions.loadUsersSuccess({ users })),
71
catchError(error => of(UserActions.loadUsersFailure({ error })))
72
)
73
)
74
)
75
);
76
77
createUser$ = createEffect(() =>
78
this.actions$.pipe(
79
ofType(UserActions.createUser),
80
switchMap(({ user }) =>
81
this.userService.createUser(user).pipe(
82
map(createdUser => UserActions.createUserSuccess({ user: createdUser })),
83
catchError(error => of(UserActions.createUserFailure({ error })))
84
)
85
)
86
)
87
);
88
89
updateUser$ = createEffect(() =>
90
this.actions$.pipe(
91
ofType(UserActions.updateUser),
92
switchMap(({ user }) =>
93
this.userService.updateUser(user).pipe(
94
map(updatedUser => UserActions.updateUserSuccess({ user: updatedUser })),
95
catchError(error => of(UserActions.updateUserFailure({ error })))
96
)
97
)
98
)
99
);
100
101
deleteUser$ = createEffect(() =>
102
this.actions$.pipe(
103
ofType(UserActions.deleteUser),
104
switchMap(({ id }) =>
105
this.userService.deleteUser(id).pipe(
106
map(() => UserActions.deleteUserSuccess({ id })),
107
catchError(error => of(UserActions.deleteUserFailure({ error })))
108
)
109
)
110
)
111
);
112
113
constructor(
114
private actions$: Actions,
115
private userService: UserService
116
) {}
117
}
118
```
119
120
**Usage Examples:**
121
122
```bash
123
# Generate user effects
124
ng generate @ngrx/schematics:effect User --creators
125
126
# Generate product effects with custom path
127
ng generate @ngrx/schematics:effect Product --path=src/app/catalog --creators
128
129
# Generate root-level auth effects
130
ng generate @ngrx/schematics:effect Auth --root --module=app.module.ts
131
```
132
133
### Non-Dispatching Effects
134
135
Effects that perform side effects without dispatching actions.
136
137
```typescript
138
// Non-dispatching effect example
139
@Injectable()
140
export class LoggingEffects {
141
142
logActions$ = createEffect(() =>
143
this.actions$.pipe(
144
tap(action => console.log('Action dispatched:', action))
145
),
146
{ dispatch: false }
147
);
148
149
saveToLocalStorage$ = createEffect(() =>
150
this.actions$.pipe(
151
ofType(UserActions.updateUserPreferences),
152
tap(({ preferences }) => {
153
localStorage.setItem('userPreferences', JSON.stringify(preferences));
154
})
155
),
156
{ dispatch: false }
157
);
158
159
constructor(private actions$: Actions) {}
160
}
161
```
162
163
### Error Handling Patterns
164
165
Comprehensive error handling strategies in generated effects.
166
167
```typescript { .api }
168
/**
169
* Error handling patterns for effects
170
*/
171
interface ErrorHandlingPatterns {
172
/** Basic catch and dispatch error action */
173
basicCatch: 'catchError(error => of(ErrorAction({ error })))';
174
/** Retry with exponential backoff */
175
retryWithBackoff: 'retryWhen(errors => errors.pipe(delay(1000), take(3)))';
176
/** Handle specific error types */
177
specificErrors: 'catchError(error => error.status === 404 ? of(NotFoundAction()) : of(ErrorAction({ error })))';
178
}
179
```
180
181
```typescript
182
// Advanced error handling
183
loadUsersWithRetry$ = createEffect(() =>
184
this.actions$.pipe(
185
ofType(UserActions.loadUsers),
186
switchMap(() =>
187
this.userService.getUsers().pipe(
188
retry(3),
189
map(users => UserActions.loadUsersSuccess({ users })),
190
catchError(error => {
191
// Log error for monitoring
192
console.error('Failed to load users:', error);
193
194
// Handle different error types
195
if (error.status === 404) {
196
return of(UserActions.noUsersFound());
197
} else if (error.status === 403) {
198
return of(UserActions.unauthorizedAccess());
199
} else {
200
return of(UserActions.loadUsersFailure({
201
error: error.message || 'Unknown error occurred'
202
}));
203
}
204
})
205
)
206
)
207
)
208
);
209
```
210
211
### Router Integration Effects
212
213
Effects that interact with Angular Router for navigation.
214
215
```typescript
216
// Router integration effects
217
import { Router } from '@angular/router';
218
219
@Injectable()
220
export class RouterEffects {
221
222
navigateToUser$ = createEffect(() =>
223
this.actions$.pipe(
224
ofType(UserActions.selectUser),
225
tap(({ userId }) => {
226
this.router.navigate(['/users', userId]);
227
})
228
),
229
{ dispatch: false }
230
);
231
232
redirectAfterLogin$ = createEffect(() =>
233
this.actions$.pipe(
234
ofType(AuthActions.loginSuccess),
235
tap(() => {
236
this.router.navigate(['/dashboard']);
237
})
238
),
239
{ dispatch: false }
240
);
241
242
constructor(
243
private actions$: Actions,
244
private router: Router
245
) {}
246
}
247
```
248
249
### Feature Effect Registration
250
251
Automatic registration of effects in Angular modules.
252
253
```typescript
254
// Generated module registration
255
import { EffectsModule } from '@ngrx/effects';
256
import { UserEffects } from './user.effects';
257
258
@NgModule({
259
imports: [
260
EffectsModule.forFeature([UserEffects])
261
]
262
})
263
export class UserModule {}
264
265
// Root module registration
266
@NgModule({
267
imports: [
268
EffectsModule.forRoot([AppEffects])
269
]
270
})
271
export class AppModule {}
272
```
273
274
### Testing Support
275
276
Generated effects include comprehensive testing utilities.
277
278
```typescript
279
// Effect testing example
280
describe('UserEffects', () => {
281
let actions$: Observable<any>;
282
let effects: UserEffects;
283
let service: jasmine.SpyObj<UserService>;
284
285
beforeEach(() => {
286
TestBed.configureTestingModule({
287
providers: [
288
UserEffects,
289
provideMockActions(() => actions$),
290
{
291
provide: UserService,
292
useValue: jasmine.createSpyObj('UserService', ['getUsers'])
293
}
294
]
295
});
296
297
effects = TestBed.inject(UserEffects);
298
service = TestBed.inject(UserService) as jasmine.SpyObj<UserService>;
299
});
300
301
describe('loadUsers$', () => {
302
it('should return loadUsersSuccess on successful load', () => {
303
const users = [{ id: '1', name: 'John' }];
304
const action = UserActions.loadUsers();
305
const outcome = UserActions.loadUsersSuccess({ users });
306
307
actions$ = hot('-a', { a: action });
308
const response = cold('-a|', { a: users });
309
service.getUsers.and.returnValue(response);
310
311
const expected = cold('--b', { b: outcome });
312
expect(effects.loadUsers$).toBeObservable(expected);
313
});
314
});
315
});
316
```
317
318
### Performance Optimizations
319
320
Generated effects include performance best practices.
321
322
```typescript { .api }
323
/**
324
* Performance optimization patterns in effects
325
*/
326
interface PerformancePatterns {
327
/** Use switchMap for cancellable operations */
328
switchMap: 'switchMap(() => service.call())';
329
/** Use mergeMap for parallel operations */
330
mergeMap: 'mergeMap(() => service.call())';
331
/** Use concatMap for sequential operations */
332
concatMap: 'concatMap(() => service.call())';
333
/** Use exhaustMap to ignore new actions while processing */
334
exhaustMap: 'exhaustMap(() => service.call())';
335
}
336
```
337
338
```typescript
339
// Optimized effect patterns
340
@Injectable()
341
export class OptimizedEffects {
342
343
// Cancel previous requests when new search is triggered
344
searchUsers$ = createEffect(() =>
345
this.actions$.pipe(
346
ofType(UserActions.searchUsers),
347
debounceTime(300), // Debounce user input
348
distinctUntilChanged(
349
(prev, curr) => prev.query === curr.query
350
),
351
switchMap(({ query }) =>
352
this.userService.searchUsers(query).pipe(
353
map(users => UserActions.searchUsersSuccess({ users })),
354
catchError(error => of(UserActions.searchUsersFailure({ error })))
355
)
356
)
357
)
358
);
359
360
// Process operations in parallel
361
loadMultipleResources$ = createEffect(() =>
362
this.actions$.pipe(
363
ofType(AppActions.loadDashboard),
364
mergeMap(() => [
365
UserActions.loadUsers(),
366
ProductActions.loadProducts(),
367
OrderActions.loadOrders()
368
])
369
)
370
);
371
372
// Prevent multiple login attempts
373
login$ = createEffect(() =>
374
this.actions$.pipe(
375
ofType(AuthActions.login),
376
exhaustMap(({ credentials }) =>
377
this.authService.login(credentials).pipe(
378
map(user => AuthActions.loginSuccess({ user })),
379
catchError(error => of(AuthActions.loginFailure({ error })))
380
)
381
)
382
)
383
);
384
}
385
```
386
387
### WebSocket Effects
388
389
Effects for real-time communication with WebSocket integration.
390
391
```typescript
392
// WebSocket effect example
393
@Injectable()
394
export class WebSocketEffects {
395
396
connectToWebSocket$ = createEffect(() =>
397
this.actions$.pipe(
398
ofType(AppActions.initializeApp),
399
switchMap(() =>
400
this.webSocketService.connect().pipe(
401
map(message => WebSocketActions.messageReceived({ message })),
402
catchError(error => of(WebSocketActions.connectionError({ error })))
403
)
404
)
405
)
406
);
407
408
sendMessage$ = createEffect(() =>
409
this.actions$.pipe(
410
ofType(WebSocketActions.sendMessage),
411
tap(({ message }) => {
412
this.webSocketService.send(message);
413
})
414
),
415
{ dispatch: false }
416
);
417
418
constructor(
419
private actions$: Actions,
420
private webSocketService: WebSocketService
421
) {}
422
}
423
```