0
# Selector Generation
1
2
NgRx selector generation schematic that creates memoized selector functions for efficiently accessing and computing derived state from the NgRx store. Selectors provide optimized, composable ways to extract and transform state data with automatic memoization.
3
4
## Capabilities
5
6
### Selector Schematic
7
8
Generates NgRx selectors using createSelector() with proper state typing and memoization.
9
10
```bash
11
# Basic selector generation
12
ng generate @ngrx/schematics:selector User
13
14
# Feature selector generation
15
ng generate @ngrx/schematics:selector User --feature
16
17
# Grouped selectors
18
ng generate @ngrx/schematics:selector User --group
19
```
20
21
```typescript { .api }
22
/**
23
* Selector schematic configuration interface
24
*/
25
interface SelectorSchema {
26
/** Name of the selector (typically entity or feature name) */
27
name: string;
28
/** Path where selector 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 selector files within folders */
35
group?: boolean;
36
/** Generate as feature selector */
37
feature?: boolean;
38
}
39
```
40
41
### Basic Selector Generation
42
43
Creates selector functions with proper state access and derived computations.
44
45
```typescript
46
// Generated selector functions
47
import { createFeatureSelector, createSelector } from '@ngrx/store';
48
import { UserState } from './user.reducer';
49
50
// Feature selector
51
export const selectUserState = createFeatureSelector<UserState>('user');
52
53
// Base selectors
54
export const selectUsers = createSelector(
55
selectUserState,
56
(state: UserState) => state.users
57
);
58
59
export const selectUsersLoading = createSelector(
60
selectUserState,
61
(state: UserState) => state.loading
62
);
63
64
export const selectUsersError = createSelector(
65
selectUserState,
66
(state: UserState) => state.error
67
);
68
69
export const selectSelectedUserId = createSelector(
70
selectUserState,
71
(state: UserState) => state.selectedUserId
72
);
73
74
// Derived selectors
75
export const selectSelectedUser = createSelector(
76
selectUsers,
77
selectSelectedUserId,
78
(users, selectedId) => users.find(user => user.id === selectedId)
79
);
80
81
export const selectActiveUsers = createSelector(
82
selectUsers,
83
(users) => users.filter(user => user.active)
84
);
85
86
export const selectUserCount = createSelector(
87
selectUsers,
88
(users) => users.length
89
);
90
91
export const selectUsersLoadingState = createSelector(
92
selectUsersLoading,
93
selectUsersError,
94
(loading, error) => ({
95
loading,
96
error,
97
loaded: !loading && !error
98
})
99
);
100
```
101
102
**Usage Examples:**
103
104
```bash
105
# Generate user selectors
106
ng generate @ngrx/schematics:selector User --feature
107
108
# Generate product selectors with custom path
109
ng generate @ngrx/schematics:selector Product --path=src/app/catalog --feature
110
111
# Generate grouped selectors
112
ng generate @ngrx/schematics:selector Order --group --feature
113
```
114
115
### Feature State Selectors
116
117
Selectors for accessing feature-specific state slices.
118
119
```typescript { .api }
120
/**
121
* Feature selector pattern for modular state access
122
*/
123
interface FeatureSelectorPattern {
124
/** Root feature selector */
125
featureSelector: 'createFeatureSelector<FeatureState>(featureKey)';
126
/** Property selectors from feature state */
127
propertySelectors: 'createSelector(featureSelector, state => state.property)';
128
/** Derived selectors combining multiple properties */
129
derivedSelectors: 'createSelector(selector1, selector2, (val1, val2) => computation)';
130
}
131
```
132
133
```typescript
134
// Feature selector example
135
export const selectAppState = createFeatureSelector<AppState>('app');
136
137
export const selectUserFeature = createSelector(
138
selectAppState,
139
(state: AppState) => state.user
140
);
141
142
export const selectProductFeature = createSelector(
143
selectAppState,
144
(state: AppState) => state.product
145
);
146
147
// Cross-feature selectors
148
export const selectUserProducts = createSelector(
149
selectUsers,
150
selectProducts,
151
selectSelectedUserId,
152
(users, products, selectedUserId) => {
153
const selectedUser = users.find(user => user.id === selectedUserId);
154
return selectedUser
155
? products.filter(product => product.userId === selectedUser.id)
156
: [];
157
}
158
);
159
```
160
161
### Entity Selectors
162
163
Specialized selectors for working with normalized entity state.
164
165
```typescript
166
// Entity selector patterns
167
import { createEntityAdapter, EntityState } from '@ngrx/entity';
168
169
export interface UserEntityState extends EntityState<User> {
170
selectedUserId: string | null;
171
loading: boolean;
172
error: string | null;
173
}
174
175
export const userAdapter = createEntityAdapter<User>();
176
177
// Entity selectors from adapter
178
export const {
179
selectIds: selectUserIds,
180
selectEntities: selectUserEntities,
181
selectAll: selectAllUsers,
182
selectTotal: selectUserTotal
183
} = userAdapter.getSelectors(selectUserState);
184
185
// Custom entity selectors
186
export const selectUserById = (id: string) => createSelector(
187
selectUserEntities,
188
(entities) => entities[id]
189
);
190
191
export const selectUsersByStatus = (status: UserStatus) => createSelector(
192
selectAllUsers,
193
(users) => users.filter(user => user.status === status)
194
);
195
```
196
197
### Complex Derived Selectors
198
199
Advanced selectors that perform computations and transformations.
200
201
```typescript
202
// Complex derived selectors
203
export const selectUserStatistics = createSelector(
204
selectAllUsers,
205
(users) => ({
206
total: users.length,
207
active: users.filter(user => user.active).length,
208
inactive: users.filter(user => !user.active).length,
209
byRole: users.reduce((acc, user) => {
210
acc[user.role] = (acc[user.role] || 0) + 1;
211
return acc;
212
}, {} as Record<string, number>)
213
})
214
);
215
216
export const selectSortedUsers = createSelector(
217
selectAllUsers,
218
(users) => users.slice().sort((a, b) => a.name.localeCompare(b.name))
219
);
220
221
export const selectFilteredUsers = (filter: UserFilter) => createSelector(
222
selectAllUsers,
223
(users) => users.filter(user => {
224
if (filter.name && !user.name.toLowerCase().includes(filter.name.toLowerCase())) {
225
return false;
226
}
227
if (filter.role && user.role !== filter.role) {
228
return false;
229
}
230
if (filter.active !== undefined && user.active !== filter.active) {
231
return false;
232
}
233
return true;
234
})
235
);
236
237
export const selectPaginatedUsers = createSelector(
238
selectSortedUsers,
239
(users, props: { page: number; pageSize: number }) => {
240
const start = props.page * props.pageSize;
241
const end = start + props.pageSize;
242
return {
243
data: users.slice(start, end),
244
total: users.length,
245
page: props.page,
246
pageSize: props.pageSize,
247
totalPages: Math.ceil(users.length / props.pageSize)
248
};
249
}
250
);
251
```
252
253
### Parameterized Selectors
254
255
Selectors that accept parameters for dynamic data access.
256
257
```typescript { .api }
258
/**
259
* Parameterized selector patterns
260
*/
261
interface ParameterizedSelectorPatterns {
262
/** Selector with props parameter */
263
withProps: 'createSelector(selector, (state, props) => computation)';
264
/** Factory function returning selector */
265
factory: '(param) => createSelector(selector, state => computation)';
266
/** Memoized parameter selector */
267
memoized: 'createSelector([selector], param => createSelector(...))';
268
}
269
```
270
271
```typescript
272
// Parameterized selectors
273
export const selectUserById = createSelector(
274
selectUserEntities,
275
(entities, props: { id: string }) => entities[props.id]
276
);
277
278
export const selectUsersByRole = (role: UserRole) => createSelector(
279
selectAllUsers,
280
(users) => users.filter(user => user.role === role)
281
);
282
283
// Memoized parameter selectors
284
const selectUsersByRoleMemoized = (() => {
285
const cache = new Map();
286
return (role: UserRole) => {
287
if (!cache.has(role)) {
288
cache.set(role, createSelector(
289
selectAllUsers,
290
(users) => users.filter(user => user.role === role)
291
));
292
}
293
return cache.get(role);
294
};
295
})();
296
```
297
298
### Selector Testing
299
300
Generated selectors include comprehensive testing support.
301
302
```typescript
303
// Selector testing examples
304
describe('User Selectors', () => {
305
const initialState: UserState = {
306
users: [
307
{ id: '1', name: 'John', active: true, role: 'admin' },
308
{ id: '2', name: 'Jane', active: false, role: 'user' }
309
],
310
loading: false,
311
error: null,
312
selectedUserId: '1'
313
};
314
315
it('should select users', () => {
316
const result = selectUsers.projector(initialState);
317
expect(result).toEqual(initialState.users);
318
});
319
320
it('should select active users', () => {
321
const result = selectActiveUsers.projector(initialState.users);
322
expect(result).toEqual([{ id: '1', name: 'John', active: true, role: 'admin' }]);
323
});
324
325
it('should select user count', () => {
326
const result = selectUserCount.projector(initialState.users);
327
expect(result).toBe(2);
328
});
329
330
it('should select selected user', () => {
331
const result = selectSelectedUser.projector(
332
initialState.users,
333
initialState.selectedUserId
334
);
335
expect(result).toEqual({ id: '1', name: 'John', active: true, role: 'admin' });
336
});
337
});
338
```
339
340
### Performance Optimizations
341
342
Generated selectors include memoization and performance best practices.
343
344
```typescript { .api }
345
/**
346
* Selector performance optimization patterns
347
*/
348
interface SelectorOptimizations {
349
/** Memoization for expensive computations */
350
memoization: 'createSelector automatically memoizes results';
351
/** Shallow equality checking */
352
shallowEqual: 'Selectors use reference equality by default';
353
/** Custom equality functions */
354
customEquality: 'createSelector(inputs, projector, equalityFn)';
355
/** Reselect optimization */
356
reselectPattern: 'Compose selectors to minimize recalculation';
357
}
358
```
359
360
```typescript
361
// Performance-optimized selectors
362
import { isEqual } from 'lodash-es';
363
364
// Custom equality for deep object comparison
365
export const selectUserPreferences = createSelector(
366
selectUserState,
367
(state: UserState) => state.preferences,
368
{
369
memoizeOptions: {
370
equalityCheck: isEqual
371
}
372
}
373
);
374
375
// Optimized composition
376
export const selectUserDashboard = createSelector(
377
selectUsers,
378
selectUsersLoading,
379
selectUsersError,
380
selectUserStatistics,
381
(users, loading, error, statistics) => ({
382
users: users.slice(0, 10), // Limit for performance
383
loading,
384
error,
385
statistics,
386
hasMore: users.length > 10
387
})
388
);
389
```
390
391
### Router Selectors
392
393
Selectors that integrate with Angular Router state.
394
395
```typescript
396
// Router integration selectors
397
import { getRouterSelectors } from '@ngrx/router-store';
398
399
export const {
400
selectCurrentRoute,
401
selectRouteParams,
402
selectRouteData,
403
selectUrl
404
} = getRouterSelectors();
405
406
export const selectUserIdFromRoute = createSelector(
407
selectRouteParams,
408
(params) => params['id']
409
);
410
411
export const selectCurrentUser = createSelector(
412
selectUsers,
413
selectUserIdFromRoute,
414
(users, userId) => users.find(user => user.id === userId)
415
);
416
```
417
418
### Selector Composition
419
420
Patterns for composing complex selectors from simpler ones.
421
422
```typescript
423
// Selector composition patterns
424
export const selectUserListViewModel = createSelector(
425
selectUsers,
426
selectUsersLoading,
427
selectUsersError,
428
selectSelectedUserId,
429
selectUserStatistics,
430
(users, loading, error, selectedId, statistics) => ({
431
users,
432
loading,
433
error,
434
selectedId,
435
statistics,
436
isEmpty: users.length === 0,
437
hasSelection: selectedId !== null,
438
selectedUser: users.find(user => user.id === selectedId)
439
})
440
);
441
442
export const selectUserFormViewModel = createSelector(
443
selectSelectedUser,
444
selectUsersLoading,
445
selectUsersError,
446
(user, loading, error) => ({
447
user,
448
isEditing: user !== undefined,
449
isLoading: loading,
450
error,
451
canSave: user !== undefined && !loading
452
})
453
);
454
```