0
# Actions and Filtering
1
2
Action stream management and type-safe action filtering capabilities for NgRx Effects, enabling precise control over which actions trigger specific effects.
3
4
## Capabilities
5
6
### Actions Service
7
8
Injectable service that provides access to the application's action stream, extending RxJS Observable with custom operators.
9
10
```typescript { .api }
11
/**
12
* Injectable service providing access to all dispatched actions
13
* Extends Observable<Action> with additional NgRx-specific functionality
14
*/
15
class Actions<V = Action> extends Observable<V> {
16
/**
17
* Custom lift method for applying operators with NgRx context
18
* @param operator - RxJS operator to apply
19
* @returns Observable with applied operator
20
*/
21
lift<R>(operator?: Operator<V, R>): Observable<R>;
22
}
23
```
24
25
**Usage Examples:**
26
27
```typescript
28
import { Injectable } from "@angular/core";
29
import { Actions } from "@ngrx/effects";
30
31
@Injectable()
32
export class MyEffects {
33
constructor(private actions$: Actions) {
34
// actions$ is an Observable<Action> containing all dispatched actions
35
this.actions$.subscribe(action => {
36
console.log('Action dispatched:', action);
37
});
38
}
39
}
40
41
// In functional effects
42
export const myEffect = createEffect(
43
(actions$ = inject(Actions)) =>
44
actions$.pipe(
45
// Process all actions
46
tap(action => console.log('All actions:', action.type))
47
),
48
{ functional: true, dispatch: false }
49
);
50
```
51
52
### ofType Operator
53
54
RxJS operator for filtering actions by type with full type safety and automatic type narrowing.
55
56
```typescript { .api }
57
/**
58
* Filters actions by string type with type narrowing
59
* @param allowedTypes - Action type strings to filter by
60
* @returns OperatorFunction that filters and narrows action types
61
*/
62
function ofType<E extends Extract<A, { type: T }>, A extends Action = Action, T extends string = A['type']>(
63
...allowedTypes: [T, ...T[]]
64
): OperatorFunction<A, E>;
65
66
/**
67
* Filters actions by ActionCreator with automatic type inference
68
* @param allowedTypes - ActionCreator instances to filter by
69
* @returns OperatorFunction that filters to specific action types
70
*/
71
function ofType<AC extends ActionCreator<string, Creator>, U extends Action = Action>(
72
...allowedTypes: [AC, ...AC[]]
73
): OperatorFunction<U, ReturnType<AC>>;
74
```
75
76
**Usage Examples:**
77
78
```typescript
79
import { ofType } from "@ngrx/effects";
80
import { createAction, props } from "@ngrx/store";
81
82
// Define actions
83
const loadBooks = createAction('[Book] Load Books');
84
const loadBooksSuccess = createAction(
85
'[Book] Load Books Success',
86
props<{ books: Book[] }>()
87
);
88
const loadBooksFailure = createAction(
89
'[Book] Load Books Failure',
90
props<{ error: string }>()
91
);
92
93
// Filter by ActionCreator (recommended)
94
loadBooks$ = createEffect(() =>
95
this.actions$.pipe(
96
ofType(loadBooks), // Type is automatically narrowed to loadBooks action
97
switchMap(() =>
98
this.bookService.getBooks().pipe(
99
map(books => loadBooksSuccess({ books })),
100
catchError(error => of(loadBooksFailure({ error: error.message })))
101
)
102
)
103
)
104
);
105
106
// Filter by multiple ActionCreators
107
bookActions$ = createEffect(() =>
108
this.actions$.pipe(
109
ofType(loadBooksSuccess, loadBooksFailure), // Union type
110
tap(action => {
111
if (action.type === loadBooksSuccess.type) {
112
console.log('Books loaded:', action.books); // Type-safe access
113
} else {
114
console.log('Load failed:', action.error); // Type-safe access
115
}
116
})
117
),
118
{ dispatch: false }
119
);
120
121
// Filter by string types (legacy approach)
122
legacyEffect$ = createEffect(() =>
123
this.actions$.pipe(
124
ofType('[User] Load User', '[User] Update User'),
125
// Actions are typed as Action with specified types
126
map(action => {
127
// Manual type checking required
128
if (action.type === '[User] Load User') {
129
// Handle load user
130
}
131
return someOtherAction();
132
})
133
)
134
);
135
```
136
137
### Advanced Filtering Patterns
138
139
**Conditional Action Processing:**
140
141
```typescript
142
conditionalEffect$ = createEffect(() =>
143
this.actions$.pipe(
144
ofType(UserActions.updateUser),
145
filter(action => action.user.isActive), // Additional filtering
146
switchMap(action =>
147
this.userService.updateUser(action.user).pipe(
148
map(() => UserActions.updateUserSuccess())
149
)
150
)
151
)
152
);
153
```
154
155
**Multiple Action Types with Different Handling:**
156
157
```typescript
158
multiActionEffect$ = createEffect(() =>
159
this.actions$.pipe(
160
ofType(
161
DataActions.loadData,
162
DataActions.refreshData,
163
DataActions.reloadData
164
),
165
switchMap(action => {
166
// Different logic based on action type
167
const force = action.type === DataActions.reloadData.type;
168
169
return this.dataService.getData({ force }).pipe(
170
map(data => DataActions.loadDataSuccess({ data })),
171
catchError(error => of(DataActions.loadDataFailure({ error })))
172
);
173
})
174
)
175
);
176
```
177
178
**Action Filtering with State Dependency:**
179
180
```typescript
181
stateAwareEffect$ = createEffect(() =>
182
this.actions$.pipe(
183
ofType(AppActions.someAction),
184
withLatestFrom(this.store.select(selectCurrentUser)),
185
filter(([action, user]) => user != null), // Only process if user exists
186
map(([action, user]) => AppActions.processWithUser({ action, user }))
187
)
188
);
189
```
190
191
**Debounced Action Processing:**
192
193
```typescript
194
debouncedSearchEffect$ = createEffect(() =>
195
this.actions$.pipe(
196
ofType(SearchActions.searchQuery),
197
debounceTime(300), // Wait 300ms between actions
198
distinctUntilChanged((prev, curr) => prev.query === curr.query),
199
switchMap(action =>
200
this.searchService.search(action.query).pipe(
201
map(results => SearchActions.searchSuccess({ results }))
202
)
203
)
204
)
205
);
206
```
207
208
## Core Types
209
210
```typescript { .api }
211
interface Action {
212
type: string;
213
}
214
215
interface ActionCreator<T extends string = string, C extends CreatorFunction<any> = CreatorFunction<any>> {
216
readonly type: T;
217
(...args: Parameters<C>): Action & ReturnType<C>;
218
}
219
220
type CreatorFunction<P> = (...args: any[]) => P;
221
222
interface Operator<T, R> {
223
(source: Observable<T>): Observable<R>;
224
}
225
226
type OperatorFunction<T, R> = (source: Observable<T>) => Observable<R>;
227
```