Skip to content

Instantly share code, notes, and snippets.

@rip222
Created April 18, 2025 15:15
Show Gist options
  • Select an option

  • Save rip222/12c9356632107f238b7aa2fdb585a030 to your computer and use it in GitHub Desktop.

Select an option

Save rip222/12c9356632107f238b7aa2fdb585a030 to your computer and use it in GitHub Desktop.
Angular signal store
import { InjectionToken, Signal, signal } from '@angular/core';
export const signalStore = <T extends object>(
initialState: T,
opts: StoreOptions = {},
) => {
const store = signal(initialState);
const patch = (partial: Partial<T> | ((current: T) => T)) => {
if (typeof partial === 'function') {
store.update(partial);
} else {
store.update((current) => ({ ...current, ...partial }));
}
};
return {
create: <R extends Record<string, unknown>>(
cb: (state: Signal<T>, patchState: typeof patch) => R,
) =>
new InjectionToken('signal-store', {
providedIn: opts.providedIn ?? null,
factory: () => ({
...cb(store.asReadonly(), patch),
reset: () => store.set(initialState),
}),
}),
};
};
export type StoreOptions = {
providedIn?: 'root' | null;
};
@draylegend
Copy link

❤️‍🔥❤️‍🔥❤️‍🔥

@rip222
Copy link
Author

rip222 commented Oct 5, 2025

Example:

const CountStore = signalStore({count: 0})
.create((state, patchState) => {
  const api = inject(CountService);

  const loadCount = api.loadCurrentCount().pipe(
    tap((count) => patchState({count}))
  ).subscribe();

  return {
       count: computed(() => state().count),
       increment: () => patchState(state => ({count: state.count + 1})),
       saveCount: () => api.saveCount(state().count)
     };
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment