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 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.
Generates NgRx selectors using createSelector() with proper state typing and memoization.
# Basic selector generation
ng generate @ngrx/schematics:selector User
# Feature selector generation
ng generate @ngrx/schematics:selector User --feature
# Grouped selectors
ng generate @ngrx/schematics:selector User --group/**
* Selector schematic configuration interface
*/
interface SelectorSchema {
/** Name of the selector (typically entity or feature name) */
name: string;
/** Path where selector files should be generated */
path?: string;
/** Angular project to target */
project?: string;
/** Generate files without creating a folder */
flat?: boolean;
/** Group selector files within folders */
group?: boolean;
/** Generate as feature selector */
feature?: boolean;
}Creates selector functions with proper state access and derived computations.
// Generated selector functions
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { UserState } from './user.reducer';
// Feature selector
export const selectUserState = createFeatureSelector<UserState>('user');
// Base selectors
export const selectUsers = createSelector(
selectUserState,
(state: UserState) => state.users
);
export const selectUsersLoading = createSelector(
selectUserState,
(state: UserState) => state.loading
);
export const selectUsersError = createSelector(
selectUserState,
(state: UserState) => state.error
);
export const selectSelectedUserId = createSelector(
selectUserState,
(state: UserState) => state.selectedUserId
);
// Derived selectors
export const selectSelectedUser = createSelector(
selectUsers,
selectSelectedUserId,
(users, selectedId) => users.find(user => user.id === selectedId)
);
export const selectActiveUsers = createSelector(
selectUsers,
(users) => users.filter(user => user.active)
);
export const selectUserCount = createSelector(
selectUsers,
(users) => users.length
);
export const selectUsersLoadingState = createSelector(
selectUsersLoading,
selectUsersError,
(loading, error) => ({
loading,
error,
loaded: !loading && !error
})
);Usage Examples:
# Generate user selectors
ng generate @ngrx/schematics:selector User --feature
# Generate product selectors with custom path
ng generate @ngrx/schematics:selector Product --path=src/app/catalog --feature
# Generate grouped selectors
ng generate @ngrx/schematics:selector Order --group --featureSelectors for accessing feature-specific state slices.
/**
* Feature selector pattern for modular state access
*/
interface FeatureSelectorPattern {
/** Root feature selector */
featureSelector: 'createFeatureSelector<FeatureState>(featureKey)';
/** Property selectors from feature state */
propertySelectors: 'createSelector(featureSelector, state => state.property)';
/** Derived selectors combining multiple properties */
derivedSelectors: 'createSelector(selector1, selector2, (val1, val2) => computation)';
}// Feature selector example
export const selectAppState = createFeatureSelector<AppState>('app');
export const selectUserFeature = createSelector(
selectAppState,
(state: AppState) => state.user
);
export const selectProductFeature = createSelector(
selectAppState,
(state: AppState) => state.product
);
// Cross-feature selectors
export const selectUserProducts = createSelector(
selectUsers,
selectProducts,
selectSelectedUserId,
(users, products, selectedUserId) => {
const selectedUser = users.find(user => user.id === selectedUserId);
return selectedUser
? products.filter(product => product.userId === selectedUser.id)
: [];
}
);Specialized selectors for working with normalized entity state.
// Entity selector patterns
import { createEntityAdapter, EntityState } from '@ngrx/entity';
export interface UserEntityState extends EntityState<User> {
selectedUserId: string | null;
loading: boolean;
error: string | null;
}
export const userAdapter = createEntityAdapter<User>();
// Entity selectors from adapter
export const {
selectIds: selectUserIds,
selectEntities: selectUserEntities,
selectAll: selectAllUsers,
selectTotal: selectUserTotal
} = userAdapter.getSelectors(selectUserState);
// Custom entity selectors
export const selectUserById = (id: string) => createSelector(
selectUserEntities,
(entities) => entities[id]
);
export const selectUsersByStatus = (status: UserStatus) => createSelector(
selectAllUsers,
(users) => users.filter(user => user.status === status)
);Advanced selectors that perform computations and transformations.
// Complex derived selectors
export const selectUserStatistics = createSelector(
selectAllUsers,
(users) => ({
total: users.length,
active: users.filter(user => user.active).length,
inactive: users.filter(user => !user.active).length,
byRole: users.reduce((acc, user) => {
acc[user.role] = (acc[user.role] || 0) + 1;
return acc;
}, {} as Record<string, number>)
})
);
export const selectSortedUsers = createSelector(
selectAllUsers,
(users) => users.slice().sort((a, b) => a.name.localeCompare(b.name))
);
export const selectFilteredUsers = (filter: UserFilter) => createSelector(
selectAllUsers,
(users) => users.filter(user => {
if (filter.name && !user.name.toLowerCase().includes(filter.name.toLowerCase())) {
return false;
}
if (filter.role && user.role !== filter.role) {
return false;
}
if (filter.active !== undefined && user.active !== filter.active) {
return false;
}
return true;
})
);
export const selectPaginatedUsers = createSelector(
selectSortedUsers,
(users, props: { page: number; pageSize: number }) => {
const start = props.page * props.pageSize;
const end = start + props.pageSize;
return {
data: users.slice(start, end),
total: users.length,
page: props.page,
pageSize: props.pageSize,
totalPages: Math.ceil(users.length / props.pageSize)
};
}
);Selectors that accept parameters for dynamic data access.
/**
* Parameterized selector patterns
*/
interface ParameterizedSelectorPatterns {
/** Selector with props parameter */
withProps: 'createSelector(selector, (state, props) => computation)';
/** Factory function returning selector */
factory: '(param) => createSelector(selector, state => computation)';
/** Memoized parameter selector */
memoized: 'createSelector([selector], param => createSelector(...))';
}// Parameterized selectors
export const selectUserById = createSelector(
selectUserEntities,
(entities, props: { id: string }) => entities[props.id]
);
export const selectUsersByRole = (role: UserRole) => createSelector(
selectAllUsers,
(users) => users.filter(user => user.role === role)
);
// Memoized parameter selectors
const selectUsersByRoleMemoized = (() => {
const cache = new Map();
return (role: UserRole) => {
if (!cache.has(role)) {
cache.set(role, createSelector(
selectAllUsers,
(users) => users.filter(user => user.role === role)
));
}
return cache.get(role);
};
})();Generated selectors include comprehensive testing support.
// Selector testing examples
describe('User Selectors', () => {
const initialState: UserState = {
users: [
{ id: '1', name: 'John', active: true, role: 'admin' },
{ id: '2', name: 'Jane', active: false, role: 'user' }
],
loading: false,
error: null,
selectedUserId: '1'
};
it('should select users', () => {
const result = selectUsers.projector(initialState);
expect(result).toEqual(initialState.users);
});
it('should select active users', () => {
const result = selectActiveUsers.projector(initialState.users);
expect(result).toEqual([{ id: '1', name: 'John', active: true, role: 'admin' }]);
});
it('should select user count', () => {
const result = selectUserCount.projector(initialState.users);
expect(result).toBe(2);
});
it('should select selected user', () => {
const result = selectSelectedUser.projector(
initialState.users,
initialState.selectedUserId
);
expect(result).toEqual({ id: '1', name: 'John', active: true, role: 'admin' });
});
});Generated selectors include memoization and performance best practices.
/**
* Selector performance optimization patterns
*/
interface SelectorOptimizations {
/** Memoization for expensive computations */
memoization: 'createSelector automatically memoizes results';
/** Shallow equality checking */
shallowEqual: 'Selectors use reference equality by default';
/** Custom equality functions */
customEquality: 'createSelector(inputs, projector, equalityFn)';
/** Reselect optimization */
reselectPattern: 'Compose selectors to minimize recalculation';
}// Performance-optimized selectors
import { isEqual } from 'lodash-es';
// Custom equality for deep object comparison
export const selectUserPreferences = createSelector(
selectUserState,
(state: UserState) => state.preferences,
{
memoizeOptions: {
equalityCheck: isEqual
}
}
);
// Optimized composition
export const selectUserDashboard = createSelector(
selectUsers,
selectUsersLoading,
selectUsersError,
selectUserStatistics,
(users, loading, error, statistics) => ({
users: users.slice(0, 10), // Limit for performance
loading,
error,
statistics,
hasMore: users.length > 10
})
);Selectors that integrate with Angular Router state.
// Router integration selectors
import { getRouterSelectors } from '@ngrx/router-store';
export const {
selectCurrentRoute,
selectRouteParams,
selectRouteData,
selectUrl
} = getRouterSelectors();
export const selectUserIdFromRoute = createSelector(
selectRouteParams,
(params) => params['id']
);
export const selectCurrentUser = createSelector(
selectUsers,
selectUserIdFromRoute,
(users, userId) => users.find(user => user.id === userId)
);Patterns for composing complex selectors from simpler ones.
// Selector composition patterns
export const selectUserListViewModel = createSelector(
selectUsers,
selectUsersLoading,
selectUsersError,
selectSelectedUserId,
selectUserStatistics,
(users, loading, error, selectedId, statistics) => ({
users,
loading,
error,
selectedId,
statistics,
isEmpty: users.length === 0,
hasSelection: selectedId !== null,
selectedUser: users.find(user => user.id === selectedId)
})
);
export const selectUserFormViewModel = createSelector(
selectSelectedUser,
selectUsersLoading,
selectUsersError,
(user, loading, error) => ({
user,
isEditing: user !== undefined,
isLoading: loading,
error,
canSave: user !== undefined && !loading
})
);