
State Implementation
- 18 installs
- 5 repo stars
- Updated January 7, 2026
- pluginagentmarketplace/custom-plugin-angular
Helps with ai & agent building tasks.
About
state-implementation is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- state-implementation
- AI & Agent Building
- AI-coding skill
State Implementation by the numbers
- 18 all-time installs (skills.sh)
- Ranked #10,736 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/pluginagentmarketplace/custom-plugin-angular --skill state-implementationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 18 |
|---|---|
| repo stars | ★ 5 |
| Last updated | January 7, 2026 |
| Repository | pluginagentmarketplace/custom-plugin-angular ↗ |
What it does
Helps with ai & agent building tasks.
Files
State Implementation Skill
Quick Start
Simple Service-Based State
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class UserStore {
private usersSubject = new BehaviorSubject<User[]>([]);
users$ = this.usersSubject.asObservable();
constructor(private http: HttpClient) {}
loadUsers() {
this.http.get<User[]>('/api/users').subscribe(
users => this.usersSubject.next(users)
);
}
addUser(user: User) {
this.http.post<User>('/api/users', user).subscribe(
newUser => {
const current = this.usersSubject.value;
this.usersSubject.next([...current, newUser]);
}
);
}
}
// Usage
export class UserListComponent {
users$ = this.userStore.users$;
constructor(private userStore: UserStore) {}
}NgRx Basics
// 1. Define actions
export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction(
'[User] Load Users Success',
props<{ users: User[] }>()
);
export const loadUsersError = createAction(
'[User] Load Users Error',
props<{ error: string }>()
);
// 2. Create reducer
const initialState: UserState = { users: [], loading: false };
export const userReducer = createReducer(
initialState,
on(loadUsers, state => ({ ...state, loading: true })),
on(loadUsersSuccess, (state, { users }) => ({
...state,
users,
loading: false
})),
on(loadUsersError, (state, { error }) => ({
...state,
error,
loading: false
}))
);
// 3. Create effect
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => loadUsersSuccess({ users })),
catchError(error => of(loadUsersError({ error })))
)
)
)
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}
// 4. Use in component
@Component({...})
export class UserListComponent {
users$ = this.store.select(selectUsers);
loading$ = this.store.select(selectLoading);
constructor(private store: Store) {
this.store.dispatch(loadUsers());
}
}NgRx Core Concepts
Store
// Dispatch action
this.store.dispatch(loadUsers());
// Select state
this.store.select(selectUsers).subscribe(users => {
console.log(users);
});
// Select with observable
this.users$ = this.store.select(selectUsers);
// Multiple selects
this.store.select(selectUsers, selectLoading).subscribe(([users, loading]) => {
// ...
});Selectors
// Feature selector
export const selectUserState = createFeatureSelector<UserState>('users');
// Select from feature
export const selectUsers = createSelector(
selectUserState,
state => state.users
);
// Selector composition
export const selectActiveUsers = createSelector(
selectUsers,
users => users.filter(u => u.active)
);
// Memoized selector
export const selectUserById = (id: number) => createSelector(
selectUsers,
users => users.find(u => u.id === id)
);
// With props
export const selectUsersByRole = createSelector(
selectUsers,
(users: User[], { role }: { role: string }) =>
users.filter(u => u.role === role)
);
// Usage with props
this.store.select(selectUsersByRole, { role: 'admin' });Effects
// Side effect - HTTP call
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersError({ error })))
)
)
)
);
// Non-dispatching effect
logActions$ = createEffect(
() => this.actions$.pipe(
tap(action => console.log(action))
),
{ dispatch: false }
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}Entity Adapter
Setup
export interface User {
id: number;
name: string;
email: string;
}
export const adapter = createEntityAdapter<User>({
selectId: (user: User) => user.id,
sortComparer: (a: User, b: User) => a.name.localeCompare(b.name)
});
export interface UserState extends EntityState<User> {
loading: boolean;
error: string | null;
}
const initialState = adapter.getInitialState({
loading: false,
error: null
});Reducer with Adapter
export const userReducer = createReducer(
initialState,
on(loadUsers, state => ({ ...state, loading: true })),
on(loadUsersSuccess, (state, { users }) =>
adapter.setAll(users, { ...state, loading: false })
),
on(addUserSuccess, (state, { user }) =>
adapter.addOne(user, state)
),
on(updateUserSuccess, (state, { user }) =>
adapter.updateOne({ id: user.id, changes: user }, state)
),
on(deleteUserSuccess, (state, { id }) =>
adapter.removeOne(id, state)
)
);
// Export selectors
export const {
selectIds,
selectEntities,
selectAll,
selectTotal
} = adapter.getSelectors(selectUserState);Facade Pattern
@Injectable()
export class UserFacade {
users$ = this.store.select(selectAllUsers);
loading$ = this.store.select(selectUsersLoading);
error$ = this.store.select(selectUsersError);
constructor(private store: Store) {}
loadUsers() {
this.store.dispatch(loadUsers());
}
addUser(user: User) {
this.store.dispatch(addUser({ user }));
}
updateUser(id: number, changes: Partial<User>) {
this.store.dispatch(updateUser({ id, changes }));
}
deleteUser(id: number) {
this.store.dispatch(deleteUser({ id }));
}
}
// Component usage simplified
@Component({...})
export class UserListComponent {
users$ = this.userFacade.users$;
loading$ = this.userFacade.loading$;
constructor(private userFacade: UserFacade) {
this.userFacade.loadUsers();
}
}Angular Signals
import { signal, computed, effect } from '@angular/core';
// Create signal
const count = signal(0);
// Read value
console.log(count()); // 0
// Update value
count.set(1);
count.update(c => c + 1);
// Computed value
const doubled = computed(() => count() * 2);
// Effect
effect(() => {
console.log(`Count is ${count()}`);
console.log(`Doubled is ${doubled()}`);
});
// Signal-based state
@Component({...})
export class CounterComponent {
count = signal(0);
doubled = computed(() => this.count() * 2);
increment() {
this.count.update(c => c + 1);
}
}HTTP Integration
HttpClient with Interceptor
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getToken();
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(authReq);
}
}
// Register
@NgModule({
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
}
]
})
export class AppModule { }Caching Strategy
@Injectable()
export class CachingService {
private cache = new Map<string, any>();
get<T>(key: string, request: Observable<T>, ttl: number = 3600000): Observable<T> {
if (this.cache.has(key)) {
return of(this.cache.get(key));
}
return request.pipe(
tap(data => {
this.cache.set(key, data);
setTimeout(() => this.cache.delete(key), ttl);
})
);
}
}
// Usage
getUsers() {
return this.caching.get(
'users',
this.http.get<User[]>('/api/users'),
5 * 60 * 1000 // 5 minutes
);
}Testing State
describe('User Store', () => {
let store: MockStore;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [StoreModule.forRoot({ users: userReducer })]
});
store = TestBed.inject(Store) as MockStore;
});
it('should load users', () => {
const action = loadUsers();
const completion = loadUsersSuccess({ users: mockUsers });
const effect$ = new UserEffects(
hot('a', { a: action }),
mockUserService
).loadUsers$;
const result = cold('b', { b: completion });
expect(effect$).toBeObservable(result);
});
it('should select users', (done) => {
store.setState({ users: { users: mockUsers } });
store.select(selectUsers).subscribe(users => {
expect(users).toEqual(mockUsers);
done();
});
});
});Best Practices
1. Normalize State: Flat structure, avoid nesting 2. Single Responsibility: Each reducer handles one feature 3. Use Facades: Simplify component-store interaction 4. Memoize Selectors: Prevent unnecessary recalculations 5. Handle Errors: Always include error states 6. Lazy Load Stores: Register feature stores when needed 7. Time-Travel Debugging: Use Redux DevTools
Advanced Patterns
Composition Pattern
// Combine multiple stores
@Injectable()
export class AppFacade {
users$ = this.userFacade.users$;
products$ = this.productFacade.products$;
cart$ = this.cartFacade.cart$;
constructor(
private userFacade: UserFacade,
private productFacade: ProductFacade,
private cartFacade: CartFacade
) {}
}Feature Flags
export const selectFeatureFlags = createFeatureSelector<FeatureFlags>('features');
export const selectFeatureEnabled = (feature: string) => createSelector(
selectFeatureFlags,
flags => flags[feature]?.enabled ?? false
);
// Component
<div *ngIf="featureEnabled$ | async">New Feature</div>Resources
angular_skill: state-management
Assets
Templates and reusable assets for state-management skill.
// NgRx Store Template
import { createAction, createReducer, createSelector, createFeatureSelector, on, props } from '@ngrx/store';
import { createEffect, Actions, ofType } from '@ngrx/effects';
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
// 1. State Interface
export interface {Feature}State extends EntityState<{Entity}> {
loading: boolean;
error: string | null;
}
// 2. Entity Adapter
export const adapter: EntityAdapter<{Entity}> = createEntityAdapter<{Entity}>({
selectId: (entity) => entity.id,
sortComparer: (a, b) => a.name.localeCompare(b.name)
});
// 3. Initial State
export const initialState: {Feature}State = adapter.getInitialState({
loading: false,
error: null
});
// 4. Actions
export const load{Feature} = createAction('[{Feature}] Load');
export const load{Feature}Success = createAction('[{Feature}] Load Success', props<{ items: {Entity}[] }>());
export const load{Feature}Failure = createAction('[{Feature}] Load Failure', props<{ error: string }>());
// 5. Reducer
export const {feature}Reducer = createReducer(
initialState,
on(load{Feature}, state => ({ ...state, loading: true })),
on(load{Feature}Success, (state, { items }) => adapter.setAll(items, { ...state, loading: false })),
on(load{Feature}Failure, (state, { error }) => ({ ...state, error, loading: false }))
);
// 6. Selectors
export const select{Feature}State = createFeatureSelector<{Feature}State>('{feature}');
export const { selectAll, selectIds, selectEntities, selectTotal } = adapter.getSelectors(select{Feature}State);
export const selectLoading = createSelector(select{Feature}State, state => state.loading);
state-management Guide
References
Documentation references for state-management skill.
NgRx Selectors Reference
Selector Creation
// Feature selector
export const selectUserState = createFeatureSelector<UserState>('users');
// Basic selector
export const selectAllUsers = createSelector(
selectUserState,
state => state.users
);
// Derived selector
export const selectActiveUsers = createSelector(
selectAllUsers,
users => users.filter(u => u.active)
);
// Parameterized selector
export const selectUserById = (id: number) => createSelector(
selectAllUsers,
users => users.find(u => u.id === id)
);Entity Adapter Selectors
const { selectAll, selectEntities, selectIds, selectTotal } = adapter.getSelectors(selectFeatureState);Usage in Component
users$ = this.store.select(selectAllUsers);
loading$ = this.store.select(selectLoading);
user$ = this.store.select(selectUserById(this.userId));#!/bin/bash
# Generate NgRx Store Files
if [ -z "$1" ]; then
echo "Usage: ./generate-store.sh <feature-name>"
exit 1
fi
FEATURE=$1
ng generate @ngrx/schematics:feature $FEATURE \
--module=app.module.ts \
--group \
--api
echo "NgRx feature '$FEATURE' generated!"
echo "Remember to add to StoreModule.forRoot()"
#!/usr/bin/env python3
import json
print(json.dumps({"skill": "state-management"}, indent=2))
Scripts
Automation scripts for state-management skill.