| name | ngrx |
| description | NgRx state management for Angular including store, effects, entity adapter, component store, and selectors. |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep |
| graph | {"domains":["domain:web-development"],"specializations":["specialization:web-development"],"skillAreas":["skill-area:application-state-management","skill-area:state-management-testing"],"roles":["role:frontend-engineer"],"topics":["topic:redux-pattern","topic:flux-architecture"]} |
NgRx Skill
Expert assistance for implementing NgRx state management in Angular applications.
Capabilities
- Configure NgRx store with feature states
- Create actions, reducers, and selectors
- Implement effects for side effects
- Use entity adapter for collections
- Apply component store for local state
- Set up NgRx DevTools integration
Usage
Invoke this skill when you need to:
- Implement centralized state management
- Handle complex async workflows
- Manage normalized entity collections
- Debug state changes with DevTools
- Scale Angular application state
Inputs
| Parameter | Type | Required | Description |
|---|
| featureName | string | Yes | Feature state name |
| entityAdapter | boolean | No | Use entity adapter |
| effects | array | No | Effects to create |
| componentStore | boolean | No | Use component store |
Configuration Example
{
"featureName": "users",
"entityAdapter": true,
"effects": ["loadUsers", "createUser", "updateUser"],
"componentStore": false
}
NgRx Patterns
Actions
import { createActionGroup, emptyProps, props } from '@ngrx/store';
export const UsersActions = createActionGroup({
source: 'Users',
events: {
'Load Users': emptyProps(),
'Load Users Success': props<{ users: User[] }>(),
'Load Users Failure': props<{ error: string }>(),
'Create User': props<{ user: CreateUserDto }>(),
'Create User Success': props<{ user: User }>(),
'Create User Failure': props<{ error: string }>(),
'Update User': props<{ id: string; changes: Partial<User> }>(),
'Update User Success': props<{ user: User }>(),
'Update User Failure': props<{ error: string }>(),
'Delete User': props<{ id: string }>(),
'Delete User Success': props<{ id: string }>(),
'Delete User Failure': props<{ error: string }>(),
: props<{ : | }>(),
},
});
Reducer with Entity Adapter
import { createReducer, on } from '@ngrx/store';
import { createEntityAdapter, EntityState } from '@ngrx/entity';
import { UsersActions } from './users.actions';
export interface User {
id: string;
name: string;
email: string;
}
export interface UsersState extends EntityState<User> {
selectedId: string | null;
loading: boolean;
error: string | null;
}
export const usersAdapter = createEntityAdapter<User>({
selectId: (user) => user.id,
sortComparer: (a, b) => a.name.localeCompare(b.name),
});
const initialState: UsersState = usersAdapter.getInitialState({
selectedId: ,
: ,
: ,
});
usersReducer = (
initialState,
(., ({
...state,
: ,
: ,
})),
(.,
usersAdapter.(users, { ...state, : })
),
(., ({
...state,
: ,
error,
})),
(.,
usersAdapter.(user, state)
),
(.,
usersAdapter.({ : user., : user }, state)
),
(.,
usersAdapter.(id, state)
),
(., ({
...state,
: id,
}))
);
Selectors
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { usersAdapter, UsersState } from './users.reducer';
export const selectUsersState = createFeatureSelector<UsersState>('users');
const { selectAll, selectEntities, selectIds, selectTotal } =
usersAdapter.getSelectors();
export const selectAllUsers = createSelector(selectUsersState, selectAll);
export const selectUserEntities = createSelector(
selectUsersState,
selectEntities
);
export const selectUsersLoading = createSelector(
selectUsersState,
(state) => state.loading
);
export const selectUsersError = createSelector(
selectUsersState,
(state) => state.error
);
export const selectSelectedUserId = createSelector(
selectUsersState,
(state) => state.selectedId
);
export const selectSelectedUser = createSelector(
selectUserEntities,
selectSelectedUserId,
(entities, selectedId) => (selectedId ? entities[selectedId] : null)
);
selectUsersCount = (selectUsersState, selectTotal);
Effects
import { Injectable, inject } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, mergeMap, switchMap, of } from 'rxjs';
import { UsersActions } from './users.actions';
import { UsersService } from '../../services/users.service';
@Injectable()
export class UsersEffects {
private actions$ = inject(Actions);
private usersService = inject(UsersService);
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UsersActions.loadUsers),
switchMap(() =>
this.usersService.getAll().pipe(
map((users) => UsersActions.loadUsersSuccess({ users })),
(
(.({ : error. }))
)
)
)
)
);
createUser$ = (
..(
(.),
(
..(user).(
( .({ user })),
(
(.({ : error. }))
)
)
)
)
);
updateUser$ = (
..(
(.),
(
..(id, changes).(
( .({ user })),
(
(.({ : error. }))
)
)
)
)
);
deleteUser$ = (
..(
(.),
(
..(id).(
( .({ id })),
(
(.({ : error. }))
)
)
)
)
);
}
Component Store (Local State)
import { Injectable } from '@angular/core';
import { ComponentStore } from '@ngrx/component-store';
import { switchMap, tap, catchError, EMPTY } from 'rxjs';
interface UserListState {
users: User[];
loading: boolean;
filter: string;
}
@Injectable()
export class UserListStore extends ComponentStore<UserListState> {
constructor(private usersService: UsersService) {
super({
users: [],
loading: false,
filter: '',
});
}
readonly users$ = this.select((state) => state.users);
readonly loading$ = this.select((state) => state.);
filter$ = .( state.);
filteredUsers$ = .(
.,
.,
users.( u..().(filter.()))
);
setFilter = .( ({
...state,
filter,
}));
loadUsers = .(
trigger$.(
( .({ : })),
(
..().(
( .({ users, : })),
( {
.({ : });
;
})
)
)
)
);
}
Store Setup
import { ApplicationConfig } from '@angular/core';
import { provideStore } from '@ngrx/store';
import { provideEffects } from '@ngrx/effects';
import { provideStoreDevtools } from '@ngrx/store-devtools';
import { usersReducer } from './store/users/users.reducer';
import { UsersEffects } from './store/users/users.effects';
export const appConfig: ApplicationConfig = {
providers: [
provideStore({
users: usersReducer,
}),
provideEffects(UsersEffects),
provideStoreDevtools({
maxAge: 25,
logOnly: false,
}),
],
};
Best Practices
- Use createActionGroup for related actions
- Leverage entity adapter for collections
- Keep effects focused on single responsibilities
- Use component store for local state
- Create granular selectors for performance
Target Processes
- angular-enterprise-development
- state-management-setup
- complex-data-flows
- enterprise-architecture