From ng
NgRx best practices for actions, reducers, selectors, and effects. Auto-invoked when working with NgRx store files.
npx claudepluginhub mayeedwin/angular-plugin --plugin ngThis skill uses the workspace's default tool permissions.
**Reference**: https://angular.dev/llms.txt | https://ngrx.io
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
Reference: https://angular.dev/llms.txt | https://ngrx.io
createAction with props<T>() — never string literals[Feature] Event Description (e.g., [Products] Load Success)export const loadProducts = createAction('[Products] Load');
export const loadProductsSuccess = createAction('[Products] Load Success', props<{ items: Product[] }>());
export const loadProductsFailure = createAction('[Products] Load Failure', props<{ error: string }>());
createReducer with typed state interfaceon() handlers must be pure — no side effects, no mutation{ ...state, changed: value }FEATURE_KEY constant alongside reducerexport const PRODUCTS_FEATURE_KEY = 'products';
export interface ProductsState {
items: Product[];
loading: boolean;
error: string | null;
}
const initialState: ProductsState = { items: [], loading: false, error: null };
export const productsReducer = createReducer(
initialState,
on(ProductActions.loadProducts, state => ({ ...state, loading: true, error: null })),
on(ProductActions.loadProductsSuccess, (state, { items }) => ({ ...state, loading: false, items })),
on(ProductActions.loadProductsFailure, (state, { error }) => ({ ...state, loading: false, error })),
);
createSelector — never access store state directly in componentscreateFeatureSelector as the root for a feature sliceexport const selectProductsState = createFeatureSelector<ProductsState>(PRODUCTS_FEATURE_KEY);
export const selectProductItems = createSelector(selectProductsState, s => s.items);
export const selectProductsLoading = createSelector(selectProductsState, s => s.loading);
// Derived selectors — compose from primitives
export const selectActiveProducts = createSelector(
selectProductItems,
items => items.filter(p => p.active)
);
Test selectors with .projector() — no store needed:
expect(selectActiveProducts.projector(mockItems)).toEqual(expected);
{ functional: true }catchError returning a failure actionswitchMap for cancellable ops (search), concatMap for ordered, mergeMap for parallel, exhaustMap for non-interruptible (login)export const loadProductsEffect = createEffect(
(actions$ = inject(Actions), service = inject(ProductsService)) =>
actions$.pipe(
ofType(ProductActions.loadProducts),
switchMap(() =>
service.getAll().pipe(
map(items => ProductActions.loadProductsSuccess({ items })),
catchError(err => of(ProductActions.loadProductsFailure({ error: err.message }))),
),
),
),
{ functional: true },
);
Use EntityAdapter for collections instead of arrays + manual manipulation:
import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';
export interface ProductsState extends EntityState<Product> {
loading: boolean;
error: string | null;
}
export const adapter: EntityAdapter<Product> = createEntityAdapter<Product>();
export const initialState: ProductsState = adapter.getInitialState({ loading: false, error: null });
// Reducer
on(ProductActions.loadProductsSuccess, (state, { items }) =>
adapter.setAll(items, { ...state, loading: false })
),
// Selectors (auto-generated)
const { selectAll, selectEntities, selectIds, selectTotal } = adapter.getSelectors();
export const selectAllProducts = createSelector(selectProductsState, selectAll);
For standalone apps, register in app.config.ts or feature providers:
// Root (app.config.ts)
provideStore(),
provideEffects(),
provideStoreDevtools({ maxAge: 25 }),
// Feature (lazy route providers)
provideState(PRODUCTS_FEATURE_KEY, productsReducer),
provideEffects([loadProductsEffect]),
export class ProductsComponent {
private readonly store = inject(Store);
readonly products = this.store.selectSignal(selectAllProducts);
readonly loading = this.store.selectSignal(selectProductsLoading);
ngOnInit() {
this.store.dispatch(ProductActions.loadProducts());
}
}
Prefer selectSignal() over select() + async pipe for signal-based components.