### Basic Store with State and Methods Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/README.md Example demonstrating how to create a basic store with state and methods using NgRx Signal Store. ```APIDOC ## Quick Start: Basic Store with State and Methods ### Description Example demonstrating how to create a basic store with state and methods using NgRx Signal Store. ### Code Example ```typescript import { signalStore, withState, withMethods } from '@ngrx/signals'; import { patchState } from '@ngrx/signals'; type CounterState = { count: number }; export const CounterStore = signalStore( withState({ count: 0 }), withMethods((store) => ({ increment(): void { patchState(store, (state) => ({ count: state.count + 1 })); }, decrement(): void { patchState(store, (state) => ({ count: state.count - 1 })); }, })) ); ``` ``` -------------------------------- ### Basic Entity Store Setup Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-entities.md Demonstrates how to initialize a SignalStore with the withEntities feature to manage a collection of entities. This setup automatically provides signals for entity IDs, the entity map, and the computed list of entities. ```typescript import { signalStore } from '@ngrx/signals'; import { withEntities } from '@ngrx/signals/entities'; type Book = { id: number; title: string; author: string; }; export const BooksStore = signalStore( withEntities() ); // Usage const store = inject(BooksStore); console.log(store.ids()); // Signal console.log(store.entityMap()); // Signal> console.log(store.entities()); // Signal ``` -------------------------------- ### Manual SignalStore Provision Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Demonstrates how to manually provide a SignalStore, for instance, during application bootstrapping. ```typescript bootstrapApplication(AppComponent, { providers: [GlobalStore], }); ``` -------------------------------- ### Root-Level SignalStore Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Example of creating a SignalStore that is provided at the root level, making it a global singleton. ```typescript export const AppStore = signalStore( { providedIn: 'root' }, withState({ theme: 'light' }) ); ``` -------------------------------- ### Basic Custom Feature Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/signal-store-feature.md An example demonstrating how to create a custom feature using `signalStoreFeature`, `withState`, and `withComputed` to manage request status. ```APIDOC ## Basic Custom Feature Example ```typescript import { signalStoreFeature, withState, withComputed } from '@ngrx/signals'; import { computed } from '@angular/core'; export type RequestStatusState = { requestStatus: 'idle' | 'pending' | 'fulfilled' | { error: string }; }; export function withRequestStatus() { return signalStoreFeature( withState({ requestStatus: 'idle', }), withComputed(({ requestStatus }) => ({ isPending: computed(() => requestStatus() === 'pending'), isFulfilled: computed(() => requestStatus() === 'fulfilled'), error: computed(() => { const status = requestStatus(); return typeof status === 'object' ? status.error : null; }), })) ); } ``` ``` -------------------------------- ### Complete Example: Book Search Store and Component Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/events.md A full example showcasing event creation, store configuration with reducers and effects, and a component that dispatches and reacts to events. This includes defining event groups for different sources and using `withReducer` and `withEffects`. ```typescript // events.ts import { event, eventGroup, type } from '@ngrx/signals/events'; export const bookSearchEvents = eventGroup({ source: 'Book Search Page', events: { opened: type(), queryChanged: type(), }, }); export const booksApiEvents = eventGroup({ source: 'Books API', events: { loadedSuccess: type(), loadedFailure: type(), }, }); // store.ts import { signalStore, withState } from '@ngrx/signals'; import { withReducer, withEffects, on, Events, Dispatcher } from '@ngrx/signals/events'; import { switchMap } from 'rxjs'; import { mapResponse } from '@ngrx/operators'; import { inject } from '@angular/core'; type State = { query: string; books: Book[]; isLoading: boolean; }; export const BookSearchStore = signalStore( withState({ query: '', books: [], isLoading: false, }), withReducer( on(bookSearchEvents.opened, () => ({ isLoading: true })), on(bookSearchEvents.queryChanged, ({ payload: query }) => ({ query, isLoading: true, })), on(booksApiEvents.loadedSuccess, ({ payload: books }) => ({ books, isLoading: false, })), on(booksApiEvents.loadedFailure, () => ({ isLoading: false, })) ), withEffects( (store, events = inject(Events), apiService = inject(BooksService)) => ({ loadBooks$: events .on(bookSearchEvents.opened, bookSearchEvents.queryChanged) .pipe( switchMap(() => apiService.search(store.query()).pipe( mapResponse({ next: (books) => booksApiEvents.loadedSuccess(books), error: (error) => booksApiEvents.loadedFailure(error.message), }) ) ) ), }) ) ); // component.ts import { Component, inject } from '@angular/core'; import { Dispatcher } from '@ngrx/signals/events'; @Component({ selector: 'app-book-search', template: `

Book Search

@if (store.isLoading()) {

Loading...

}
    @for (book of store.books(); track book.id) {
  • {{ book.title }}
  • }
`, }) export class BookSearchComponent { readonly dispatcher = inject(Dispatcher); readonly store = inject(BookSearchStore); ngOnInit(): void { this.dispatcher.dispatch(bookSearchEvents.opened()); } onQueryChange(event: Event): void { const query = (event.target as HTMLInputElement).value; this.dispatcher.dispatch(bookSearchEvents.queryChanged(query)); } } ``` -------------------------------- ### Basic Usage Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-methods.md Demonstrates how to add simple increment, decrement, and reset methods to a counter store. ```APIDOC ## withMethods ### Description Adds methods to a SignalStore for state mutations and side effects. ### Method Signature ```typescript withMethods( methodsFactory: (store: Store, ...injected: any[]) => Record ): SignalStoreFeature ``` ### Parameters #### methodsFactory - **store** (Store) - The store instance. - **...injected** (any[]) - Injected dependencies. - **Returns** (Record) - An object containing methods. ### Description The `withMethods` feature enables adding methods to a SignalStore that can modify state, trigger side effects, or encapsulate complex logic. The factory function is executed within an injection context, allowing you to inject dependencies. Methods have full access to all previously defined state signals, computed properties, and other methods. ### Basic Usage Example ```typescript import { signalStore, withState, withMethods } from '@ngrx/signals'; import { patchState } from '@ngrx/signals'; type CounterState = { count: number; }; export const CounterStore = signalStore( withState({ count: 0 }), withMethods((store) => ({ increment(): void { patchState(store, (state) => ({ count: state.count + 1 })); }, decrement(): void { patchState(store, (state) => ({ count: state.count - 1 })); }, reset(): void { patchState(store, { count: 0 }); }, })) ); // Usage const store = inject(CounterStore); store.increment(); store.reset(); ``` ``` -------------------------------- ### Basic Usage Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-props.md Demonstrates how to use the withProps feature to expose a signal's value as an observable. ```typescript import { signalStore, withState, withProps } from '@ngrx/signals'; import { toObservable } from '@angular/core/rxjs-interop'; type CounterState = { count: number; }; export const CounterStore = signalStore( withState({ count: 0 }), withProps(({ count }) => ({ // Expose as observable for RxJS-based code count$: toObservable(count), })) ); // Usage // const store = inject(CounterStore); // console.log(store.count()); // Signal // store.count$.subscribe(c => console.log(c)); // Observable ``` -------------------------------- ### WatchState with Custom Injector Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Example of using `watchState` outside of an injection context by providing a custom `Injector`. ```typescript @Component({ /* ... */ }) export class MyComponent { readonly injector = inject(Injector); readonly store = inject(MyStore); ngOnInit(): void { watchState(this.store, console.log, { injector: this.injector, }); } } ``` -------------------------------- ### Environment-Based Configuration Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Conditionally configure the store based on the environment. This example adds logging only in development environments. ```typescript import { environment } from '../environment'; import { signalStore } from '@ngrx/signals'; export const AppStore = signalStore( { providedIn: 'root' }, withState(environment.initialState), // Add logging in development environment.production ? [] : [withLogger('AppStore')] ); ``` -------------------------------- ### Basic Usage Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-state.md Demonstrates the basic usage of the withState feature to define a UserState with id, name, and email properties, and how to access these as signals. ```APIDOC ## Basic Usage Example ### Description This example shows how to use `withState` to initialize a store with a `UserState` object and access its properties as signals. ### Code ```typescript import { signalStore, withState } from '@ngrx/signals'; type UserState = { id: number; name: string; email: string; }; export const UserStore = signalStore( withState({ id: 0, name: '', email: '', }) ); // In a component const store = inject(UserStore); console.log(store.id()); // Signal console.log(store.name()); // Signal console.log(store.email()); // Signal ``` ``` -------------------------------- ### Factory Function Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-state.md Shows how to use a factory function with withState to initialize state, allowing for dependency injection within the factory. ```APIDOC ## Factory Function Example ### Description This example illustrates using a factory function with `withState` to lazily initialize the state, which can leverage dependency injection. ### Code ```typescript import { signalStore, withState } from '@ngrx/signals'; import { InjectionToken, inject } from '@angular/core'; const INITIAL_CONFIG = new InjectionToken('initialConfig', { factory: () => loadAppConfig(), }); type AppState = { config: AppConfig; }; export const AppStore = signalStore( withState(() => ({ config: inject(INITIAL_CONFIG), })) ); ``` ``` -------------------------------- ### Observable Conversion Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-props.md Shows how to convert signals like query, results, and resultCount into observables using withProps and toObservable. ```typescript import { signalStore, withState, withComputed, withProps } from '@ngrx/signals'; import { toObservable } from '@angular/core/rxjs-interop'; import { computed } from '@angular/core'; type SearchState = { query: string; results: any[]; }; export const SearchStore = signalStore( withState({ query: '', results: [] }), withComputed(({ query, results }) => ({ resultCount: computed(() => results().length), })), withProps(({ query, results, resultCount }) => ({ query$: toObservable(query), results$: toObservable(results), resultCount$: toObservable(resultCount), })) ); // Usage // const store = inject(SearchStore); // store.query$.pipe( // debounceTime(300), // distinctUntilChanged() // ).subscribe(query => console.log('Search:', query)); ``` -------------------------------- ### Grouping Dependencies Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-props.md Illustrates using withProps to group injected services like ApiService and LoggerService within a SignalStore. ```typescript import { signalStore, withState, withProps, withMethods } from '@ngrx/signals'; import { inject } from '@angular/core'; import { patchState } from '@ngrx/signals'; import { ApiService } from './api.service'; import { LoggerService } from './logger.service'; type AppState = { items: any[]; }; export const AppStore = signalStore( withState({ items: [] }), withProps(() => ({ apiService: inject(ApiService), logger: inject(LoggerService), })), withMethods(({ apiService, logger, ...store }) => ({ async loadItems(): Promise { logger.info('Loading items...'); try { const items = await apiService.getItems(); patchState(store, { items }); logger.info('Items loaded successfully'); } catch (error) { logger.error('Failed to load items', error); } }, })) ); // Usage // const store = inject(AppStore); // store.loadItems(); ``` -------------------------------- ### Basic Usage Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/rx-method.md Demonstrates how to use rxMethod to create a debounced search method within a Signal Store, including setting loading states and handling results. ```APIDOC ## Basic Usage Example ```typescript import { signalStore, withState, withMethods } from '@ngrx/signals'; import { rxMethod } from '@ngrx/signals/rxjs-interop'; import { debounceTime, distinctUntilChanged, pipe, switchMap, tap } from 'rxjs'; import { patchState } from '@ngrx/signals'; type SearchState = { query: string; results: any[]; isLoading: boolean; }; export const SearchStore = signalStore( withState({ query: '', results: [], isLoading: false, }), withMethods((store) => ({ search: rxMethod( pipe( // Debounce input debounceTime(300), // Only search when query changes distinctUntilChanged(), // Set loading state tap(() => patchState(store, { isLoading: true })), // Switch to search switchMap((query) => { patchState(store, { query }); return performSearch(query).pipe( tap((results) => { patchState(store, { results, isLoading: false }); }) ); }) ) ), })) ); // Usage const store = inject(SearchStore); store.search('angular'); store.search('signals'); ``` ``` -------------------------------- ### Basic Usage Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-entities.md Demonstrates the basic usage of the withEntities feature to create a store that manages a collection of books, providing signals for IDs, entity map, and entities. ```APIDOC ## Basic Usage Example ### Description This example shows how to initialize a `BooksStore` using `withEntities` to manage a collection of `Book` objects. It demonstrates how to access the automatically generated signals for `ids`, `entityMap`, and `entities`. ### Code ```typescript import { signalStore } from '@ngrx/signals'; import { withEntities } from '@ngrx/signals/entities'; type Book = { id: number; title: string; author: string; }; export const BooksStore = signalStore( withEntities() ); // Usage const store = inject(BooksStore); console.log(store.ids()); // Signal console.log(store.entityMap()); // Signal> console.log(store.entities()); // Signal ``` ``` -------------------------------- ### withHooks Factory Form Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Demonstrates using the factory form of `withHooks`, which allows injecting dependencies to configure hooks. ```typescript withHooks((store) => { const service = inject(MyService); return { onInit() { /* ... */ }, onDestroy() { /* ... */ }, }; }) ``` -------------------------------- ### Global Store Registration Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/signal-store.md Registers a store globally using `providedIn: 'root'`. This example demonstrates setting initial state for theme and sidebar collapsed status. ```typescript import { signalStore, withState } from '@ngrx/signals'; type AppState = { theme: 'light' | 'dark'; sidebarCollapsed: boolean; }; export const AppStore = signalStore( { providedIn: 'root' }, // Global registration withState({ theme: 'light', sidebarCollapsed: false, }) ); ``` -------------------------------- ### Integrate Logger Feature into BooksStore Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/custom-store-features.md This example shows how to use the `withLogger` custom feature when defining the `BooksStore`. ```typescript import { signalStore } from '@ngrx/signals'; import { withEntities } from '@ngrx/signals/entities'; import { withRequestStatus } from './with-request-status'; import { withLogger } from './with-logger'; import { Book } from './book'; export const BooksStore = signalStore( withEntities(), withRequestStatus(), withLogger('books') ); ``` -------------------------------- ### Integrate Selected Entity Feature into BooksStore Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/custom-store-features.md This example demonstrates integrating the `withSelectedEntity` feature into the `BooksStore`, ensuring `withEntities` is also used. ```typescript import { signalStore } from '@ngrx/signals'; import { withEntities } from '@ngrx/signals/entities'; import { withSelectedEntity } from './with-selected-entity'; import { Book } from './book'; export const BooksStore = signalStore( withEntities(), withSelectedEntity() ); ``` -------------------------------- ### Basic Usage Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-computed.md Demonstrates how to use `withComputed` to add computed signals like `todoCount`, `completedCount`, `pendingCount`, and `completionPercentage` to a `TodoStore`. ```APIDOC ## Basic Usage Example ### Description This example shows how to create a `TodoStore` using `withState` and `withComputed` to derive various counts and completion percentages from the `todos` state. ### Usage ```typescript import { signalStore, withState, withComputed } from '@ngrx/signals'; import { computed } from '@angular/core'; type TodoState = { todos: Array<{ id: number; title: string; completed: boolean }>; }; export const TodoStore = signalStore( withState({ todos: [], }), withComputed(({ todos }) => ({ todoCount: computed(() => todos().length), completedCount: computed(() => todos().filter(t => t.completed).length ), pendingCount: computed(() => todos().filter(t => !t.completed).length ), completionPercentage: computed(() => { const total = todos().length; const completed = todos().filter(t => t.completed).length; return total === 0 ? 0 : (completed / total) * 100; }), })) ); // Usage // const store = inject(TodoStore); // console.log(store.todoCount()); // Signal // console.log(store.completionPercentage()); // Signal ``` ``` -------------------------------- ### Using Custom Features in Stores Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/signal-store-feature.md An example showing how to integrate a custom feature (`withRequestStatus`) into a `signalStore` along with methods for data loading. ```APIDOC ## Using Custom Features in Stores ```typescript import { signalStore, withMethods } from '@ngrx/signals'; import { patchState } from '@ngrx/signals'; import { withRequestStatus } from './with-request-status'; export const DataStore = signalStore( withRequestStatus(), withMethods((store) => ({ async loadData(): Promise { patchState(store, { requestStatus: 'pending' }); try { const data = await fetchData(); patchState(store, { requestStatus: 'fulfilled', data, }); } catch (error: any) { patchState(store, { requestStatus: { error: error.message }, }); } }, })) ); ``` ``` -------------------------------- ### Creating a State Audit Trail with watchState Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/watch-state.md This example demonstrates how to use watchState to record every state change for auditing purposes. An AuditService is injected to handle the recording of state entries, including a timestamp, the state itself, and a hash of the state. ```typescript import { signalStore, withState, withHooks } from '@ngrx/signals'; import { watchState } from '@ngrx/signals'; import { inject } from '@angular/core'; interface AuditEntry { timestamp: Date; state: any; hash: string; } class AuditService { private entries: AuditEntry[] = []; record(state: any): void { this.entries.push({ timestamp: new Date(), state: structuredClone(state), hash: this.getHash(state), }); } private getHash(state: any): string { return JSON.stringify(state) .split('') .reduce((a, b) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0) .toString(); } getAudit(): AuditEntry[] { return this.entries; } } export const AuditedStore = signalStore( withState({ data: '', modified: false }), withHooks((store) => { const auditService = inject(AuditService); return { onInit() { watchState(store, (state) => { auditService.record(state); }); }, }; }) ); ``` -------------------------------- ### Implementing Undo/Redo with watchState Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/watch-state.md This example shows how to leverage watchState to build undo/redo functionality by maintaining a history of state changes. The StateHistory class manages the history stack, and watchState ensures it's updated on every state modification. ```typescript import { signalStore, withState, withMethods, withHooks } from '@ngrx/signals'; import { patchState, watchState, getState } from '@ngrx/signals'; type HistoryState = { value: number; }; class StateHistory { private history: HistoryState[] = []; private index = -1; push(state: HistoryState): void { // Remove any redo history this.history.splice(this.index + 1); this.history.push(state); this.index++; } undo(): HistoryState | undefined { if (this.index > 0) { this.index--; return this.history[this.index]; } return undefined; } redo(): HistoryState | undefined { if (this.index < this.history.length - 1) { this.index++; return this.history[this.index]; } return undefined; } } export const HistoryStore = signalStore( withState({ value: 0 }), withMethods((store) => ({ increment(): void { patchState(store, (state) => ({ value: state.value + 1 })); }, undo(): void { const previous = history.undo(); if (previous) { patchState(store, previous); } }, redo(): void { const next = history.redo(); if (next) { patchState(store, next); } }, })), withHooks({ onInit(store) { const history = new StateHistory(); history.push(getState(store)); watchState(store, (state) => { history.push(state); }); }, }) ); ``` -------------------------------- ### Bidirectional Synchronization with External WritableSignal Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-linked-state.md This example shows how to achieve bidirectional synchronization between an external `writableSignal` and a store's state slice. It's useful for integrating with external form controls or other signal-based data sources. ```typescript import { signalStore, withState, withLinkedState, withMethods } from '@ngrx/signals'; import { writableSignal } from '@angular/core'; import { patchState } from '@ngrx/signals'; type FormState = { formData: { name: string; email: string }; }; export const FormStore = signalStore( withState({ formData: { name: '', email: '' }, }), withLinkedState(({ formData }) => ({ // External WritableSignal synchronized with store externalForm: writableSignal({ name: '', email: '' }), })), withMethods((store) => ({ updateFromExternal(): void { const external = store.externalForm(); patchState(store, { formData: external }); }, })) ); ``` -------------------------------- ### Multiple Computed Signals Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-computed.md Shows how to stack multiple `withComputed` features to create complex derived state, as demonstrated in the `ComplexStore` example. ```APIDOC ## Multiple Computed Signals ### Description This example illustrates how multiple `withComputed` features can be chained together within a `signalStore` definition to build more complex derived state logic, such as finding a `selectedUser` and then deriving `otherUsers` based on that selection. ### Usage ```typescript import { signalStore, withState, withComputed } from '@ngrx/signals'; export const ComplexStore = signalStore( withState({ users: [], selectedUserId: null, }), withComputed(({ users, selectedUserId }) => ({ selectedUser: computed(() => { const id = selectedUserId(); return id ? users().find(u => u.id === id) : null; }), })), withComputed(({ users, selectedUser }) => ({ otherUsers: computed(() => { const selected = selectedUser(); return selected ? users().filter(u => u.id !== selected.id) : users(); }), })) ); ``` ``` -------------------------------- ### Nested State Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-state.md Illustrates how withState handles nested objects by creating DeepSignals, allowing access to deeply nested properties as signals. ```APIDOC ## Nested State Example ### Description This example demonstrates how `withState` automatically creates `DeepSignals` for nested state properties, enabling granular signal access. ### Code ```typescript import { signalStore, withState } from '@ngrx/signals'; type FilterState = { query: string; sort: { field: string; order: 'asc' | 'desc'; }; }; export const FilterStore = signalStore( withState({ query: '', sort: { field: 'name', order: 'asc', }, }) ); // Accessing nested signals const store = inject(FilterStore); console.log(store.query()); // Signal console.log(store.sort()); // DeepSignal<{ field: string; order: 'asc' | 'desc' }> console.log(store.sort.field()); // Signal console.log(store.sort.order()); // Signal<'asc' | 'desc'> ``` ``` -------------------------------- ### Pagination Pattern Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/README.md Implement pagination for collections using `withState` for page and page size, and `withComputed` to derive the paginated items. This example slices the entities array. ```typescript withState<{ page: number; pageSize: number }> ({ page: 1, pageSize: 10 }), withComputed(({ entities, page, pageSize }) => ({ paginatedItems: computed(() => { const items = entities(); const start = (page() - 1) * pageSize(); return items.slice(start, start + pageSize()); }), })) ``` -------------------------------- ### Basic Usage of getState Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/get-state.md Demonstrates how to retrieve the entire state of a simple counter store using `getState` within an Angular effect. This example shows the fundamental use case for reading the complete state. ```typescript import { signalStore, withState } from '@ngrx/signals'; import { getState } from '@ngrx/signals'; import { effect } from '@angular/core'; type CounterState = { count: number; }; export const CounterStore = signalStore( withState({ count: 0 }) ); // In a component const store = inject(CounterStore); effect(() => { const state = getState(store); console.log('Current state:', state); // { count: 0 } }); ``` -------------------------------- ### Explicit Linking with linkedSignal() Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-linked-state.md This example shows explicit linking using `linkedSignal()`, which allows for more complex synchronization logic. It's useful when you need to maintain a selected item based on a source array, keeping the selection if possible. ```typescript import { signalStore, withState, withLinkedState } from '@ngrx/signals'; import { linkedSignal } from '@angular/core'; type ItemsState = { items: Array<{ id: number; label: string }>; }; interface Item { id: number; label: string; } export const ItemStore = signalStore( withState({ items: [] }), withLinkedState(({ items }) => ({ selectedItem: linkedSignal({ source: items, computation: (newItems, previous) => { // Keep selected item if it still exists const itemExists = newItems.find(i => i.id === previous?.value.id); return itemExists ?? newItems[0]; }, }), })) ); ``` -------------------------------- ### Component-Level SignalStore Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Example of injecting a SignalStore within a component, typically for component-scoped state management. ```typescript @Component({ providers: [ComponentStore], }) export class MyComponent { readonly store = inject(ComponentStore); } ``` -------------------------------- ### Capturing State Snapshot for Async Operations Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/get-state.md This example shows how to capture a snapshot of the current state using `getState` before initiating an asynchronous operation. The snapshot can be used for logging, comparison, or error handling if the operation fails. ```typescript import { signalStore, withState, withMethods } from '@ngrx/signals'; import { getState } from '@ngrx/signals'; import { patchState } from '@ngrx/signals'; type RequestState = { data: any; timestamp: Date; }; export const AsyncStore = signalStore( withState({ data: null, timestamp: new Date(), }), withMethods((store) => ({ async refresh(): Promise { // Capture current state as snapshot const snapshot = getState(store); try { const newData = await fetchData(); // Use snapshot for comparison or logging console.log('Updated from state:', snapshot); patchState(store, { data: newData, timestamp: new Date(), }); } catch (error) { console.error('Update failed from state:', snapshot); } }, })) ); ``` -------------------------------- ### Invalid State Shape Examples Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/types.md Shows examples of invalid state types that are not plain objects, such as `Map` or `Set`. ```typescript // ❌ Invalid - not a record type InvalidState = Map; type InvalidState2 = Set; ``` -------------------------------- ### withHooks Object Form Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Example of using the object form of `withHooks` to directly define lifecycle hooks like `onInit` and `onDestroy`. ```typescript withHooks({ onInit(store) { /* ... */ }, onDestroy(store) { /* ... */ }, }) ``` -------------------------------- ### Custom Entity ID Selection Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Example of using `withEntities` with a custom `selectId` function to extract IDs from entities with non-standard ID fields. ```typescript type User = { userId: string; // Non-standard ID field name: string; }; withEntities({ selectId: (user) => user.userId, }) ``` -------------------------------- ### Error Example: Missing EntityState Properties Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/custom-store-features.md This example illustrates a compilation error that occurs when `withSelectedEntity` is used without the necessary `EntityState` properties (like `entityMap` and `ids`) being defined in the store. ```typescript import { signalStore } from '@ngrx/signals'; import { withSelectedEntity } from './with-selected-entity'; import { Book } from './book'; export const BooksStore = signalStore( withState({ books: [] as Book[], isLoading: false }), // Error: `EntityState` properties (`entityMap` and `ids`) are missing in the `BooksStore`. withSelectedEntity() ); ``` -------------------------------- ### Methods with Parameters Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-methods.md Shows how to define methods that accept parameters for actions like adding, toggling, or removing todos. ```APIDOC ## withMethods with Parameters ### Description Adds methods to a SignalStore that accept parameters for state mutations. ### Methods - **addTodo**(title: string): void - **toggleTodo**(id: number): void - **removeTodo**(id: number): void ### Example Usage ```typescript import { signalStore, withState, withMethods } from '@ngrx/signals'; import { patchState } from '@ngrx/signals'; type TodoState = { todos: Array<{ id: number; title: string; completed: boolean }>; }; export const TodoStore = signalStore( withState({ todos: [] }), withMethods((store) => ({ addTodo(title: string): void { const newTodo = { id: Date.now(), title, completed: false, }; patchState(store, (state) => ({ todos: [...state.todos, newTodo], })); }, toggleTodo(id: number): void { patchState(store, (state) => ({ todos: state.todos.map(t => t.id === id ? { ...t, completed: !t.completed } : t ), })); }, removeTodo(id: number): void { patchState(store, (state) => ({ todos: state.todos.filter(t => t.id !== id), })); }, })) ); ``` ``` -------------------------------- ### Basic Counter Store with Hooks Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-hooks.md Demonstrates a simple counter store with onInit and onDestroy hooks for logging. ```typescript import { signalStore, withState, withHooks } from '@ngrx/signals'; import { interval } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export const CounterStore = signalStore( withState({ count: 0 }), withHooks({ onInit(store) { console.log('CounterStore initialized'); }, onDestroy(store) { console.log('CounterStore destroyed, final count:', store.count()); }, }) ); ``` -------------------------------- ### Basic Usage of watchState Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/watch-state.md Demonstrates how to use watchState within a store's onInit hook to log state changes to the console. Ensure necessary imports from '@ngrx/signals' are included. ```typescript import { signalStore, withState, withMethods, withHooks } from '@ngrx/signals'; import { patchState, watchState } from '@ngrx/signals'; export const CounterStore = signalStore( withState({ count: 0 }), withMethods((store) => ({ increment(): void { patchState(store, (state) => ({ count: state.count + 1 })); }, })), withHooks({ onInit(store) { watchState(store, (state) => { console.log('State changed:', state); }); }, }) ); // Usage const store = inject(CounterStore); store.increment(); // Logs: State changed: { count: 1 } store.increment(); // Logs: State changed: { count: 2 } ``` -------------------------------- ### Core Signal Store Creation and Features Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/INDEX.md Demonstrates the basic creation of a signal store and the addition of common features like state, computed signals, and methods. Use these as building blocks for your stores. ```typescript // Create a store signalStore(feature1, feature2, ...) // Add state withState(initialState) // Add computed signals withComputed(({ state }) => ({ computed: ... })) // Add methods withMethods((store) => ({ method: ... })) // Update state patchState(store, { ...updates }) // Read state const state = getState(store); // Work with entities withEntities() patchState(store, addEntity(entity)) ``` -------------------------------- ### Unprotected State SignalStore Example Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Shows how to create a SignalStore with unprotected state, allowing external modifications using `patchState`. ```typescript export const FlexibleStore = signalStore( { protectedState: false }, withState({ count: 0 }) ); // External modification (only works with protectedState: false) const store = inject(FlexibleStore); patchState(store, { count: 10 }); // Allowed if protectedState is false ``` -------------------------------- ### Error Handling in Store Initialization Hooks Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-hooks.md Demonstrates implementing robust error handling within the `onInit` hook using a try-catch block. This ensures that initialization failures are logged without crashing the application. ```typescript import { signalStore, withState, withHooks } from '@ngrx/signals'; import { inject } from '@angular/core'; import { LoggerService } from './logger.service'; export const SafeInitStore = signalStore( withState({ initialized: false }), withHooks((store) => { const logger = inject(LoggerService); return { onInit() { try { // Initialization logic logger.info('Store initialized successfully'); } catch (error) { logger.error('Failed to initialize store', error); } }, }; }) ); ``` -------------------------------- ### Updating Entities Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-entities.md Provides examples of updating single or multiple entities within a store managed by `withEntities`, using `updateEntity` and `updateEntities`. ```APIDOC ## Updating Entities ### Description This example demonstrates how to update entities within a store using `updateEntity` for single entity updates and `updateEntities` for batch updates. It shows updating specific fields, using a function to derive changes, and applying updates based on a predicate. ### Code ```typescript import { signalStore, withMethods } from '@ngrx/signals'; import { withEntities, updateEntity, updateEntities } from '@ngrx/signals/entities'; import { patchState } from '@ngrx/signals'; type Article = { id: number; title: string; published: boolean; }; export const ArticleStore = signalStore( withEntities
(), withMethods((store) => ({ publishArticle(id: number): void { patchState( store, updateEntity({ id, changes: { published: true } }) ); }, updateTitle(id: number, title: string): void { patchState( store, updateEntity({ id, changes: (article) => ({ title: title.toUpperCase(), }), }) ); }, publishAll(): void { patchState( store, updateEntities({ predicate: () => true, changes: { published: true }, }) ); }, })) ); ``` ``` -------------------------------- ### Logging State Changes on Initialization Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/get-state.md Illustrates using `getState` within `withHooks` and an `effect` to log the store's state whenever it changes, specifically triggered on initialization. This is helpful for initial state inspection. ```typescript import { signalStore, withState, withHooks } from '@ngrx/signals'; import { getState } from '@ngrx/signals'; import { effect } from '@angular/core'; export const LoggedStore = signalStore( withState({ value: 0, timestamp: Date.now() }), withHooks({ onInit(store) { effect(() => { const state = getState(store); console.log('[StateLog]', new Date().toISOString(), state); }); }, }) ); ``` -------------------------------- ### Angular Signal with Custom Equality Function Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/configuration.md Example of creating an Angular signal with a custom equality function to control when updates trigger. ```typescript import { signal } from '@angular/core'; const count = signal(0, { equal: (a, b) => a === b }); const data = signal([], { equal: (a, b) => JSON.stringify(a) === JSON.stringify(b) }); ``` -------------------------------- ### Basic Usage of rxMethod with Debounce and SwitchMap Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/rx-method.md Demonstrates how to use rxMethod to create a search method that debounces user input, prevents duplicate searches, and handles asynchronous results with loading states. ```typescript import { signalStore, withState, withMethods } from '@ngrx/signals'; import { rxMethod } from '@ngrx/signals/rxjs-interop'; import { debounceTime, distinctUntilChanged, pipe, switchMap, tap } from 'rxjs'; import { patchState } from '@ngrx/signals'; type SearchState = { query: string; results: any[]; isLoading: boolean; }; export const SearchStore = signalStore( withState({ query: '', results: [], isLoading: false, }), withMethods((store) => ({ search: rxMethod( pipe( // Debounce input debounceTime(300), // Only search when query changes distinctUntilChanged(), // Set loading state tap(() => patchState(store, { isLoading: true })), // Switch to search switchMap((query) => { patchState(store, { query }); return performSearch(query).pipe( tap((results) => { patchState(store, { results, isLoading: false }); }) ); }) ) ), })) ); // Usage const store = inject(SearchStore); store.search('angular'); store.search('signals'); ``` -------------------------------- ### Feature-Based Composition with signalStoreFeature Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/README.md Create reusable store features using `signalStoreFeature`. This example defines a feature for managing request status. ```typescript export function withRequestStatus() { return signalStoreFeature( withState({ requestStatus: 'idle' }), withComputed(({ requestStatus }) => ({ isPending: computed(() => requestStatus() === 'pending'), })) ); } // Reuse across multiple stores export const DataStore = signalStore( withRequestStatus(), // ... other features ); ``` -------------------------------- ### Real-time Sync to Storage with watchState Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/watch-state.md Use `watchState` within `onInit` to immediately synchronize store state changes to `localStorage`. This is useful for persisting user preferences or application settings. ```typescript import { signalStore, withState, withHooks } from '@ngrx/signals'; import { watchState } from '@ngrx/signals'; export const PersistentStore = signalStore( withState({ preferences: { theme: 'light', fontSize: 14, }, }), withHooks({ onInit(store) { watchState(store, (state) => { // Sync immediately to storage localStorage.setItem('preferences', JSON.stringify(state.preferences)); }); }, }) ); ``` -------------------------------- ### Feature with Required Input Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/signal-store-feature.md An example of a feature that requires specific input state shape using `type` and `signalStoreFeature`, demonstrated with `withSelectedEntity`. ```APIDOC ## Feature with Required Input ```typescript import { signalStoreFeature, type, withState, withComputed } from '@ngrx/signals'; import { computed } from '@angular/core'; import { EntityState } from '@ngrx/signals/entities'; // Feature that requires EntityState input export function withSelectedEntity() { return signalStoreFeature( { state: type>() }, withState<{ selectedEntityId: string | number | null }>({ selectedEntityId: null, }), withComputed(({ entityMap, selectedEntityId }) => ({ selectedEntity: computed(() => { const id = selectedEntityId(); return id ? entityMap()[id] : null; }), })) ); } // Usage with entities import { signalStore } from '@ngrx/signals'; import { withEntities } from '@ngrx/signals/entities'; export const UserStore = signalStore( withEntities(), withSelectedEntity() ); ``` ``` -------------------------------- ### Singleton Service Pattern Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-props.md Shows how to use withProps to inject and expose a singleton service, like CacheService, within a SignalStore. ```typescript import { signalStore, withState, withProps } from '@ngrx/signals'; import { inject } from '@angular/core'; class CacheService { private cache = new Map(); get(key: string): any { return this.cache.get(key); } set(key: string, value: any): void { this.cache.set(key, value); } } export const CachedStore = signalStore( withState({ data: [] }), withProps(() => ({ cache: inject(CacheService), })) ); // Usage // const store = inject(CachedStore); // store.cache.set('myKey', 'myValue'); // const cached = store.cache.get('myKey'); ``` -------------------------------- ### Valid State Shape Examples Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/types.md Illustrates valid type definitions for state in Signal Store, emphasizing the requirement for plain objects (records). ```typescript // ✅ Valid state types type ValidState = { count: number; items: Item[]; filter: { query: string; order: 'asc' | 'desc' }; }; ``` -------------------------------- ### Async Methods with Promises Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-methods.md Illustrates creating asynchronous methods that handle promises, such as fetching data from an API. ```APIDOC ## Async Methods with Promises ### Description Defines asynchronous methods within a SignalStore that return Promises, typically used for operations like data fetching. ### Method Signature ```typescript async loadData(): Promise ``` ### Dependencies - `HttpClient` (injected) ### Example Usage ```typescript import { signalStore, withState, withMethods, inject } from '@ngrx/signals'; import { patchState } from '@ngrx/signals'; import { HttpClient } from '@angular/common/http'; type DataState = { data: any[]; isLoading: boolean; error: string | null; }; export const DataStore = signalStore( withState({ data: [], isLoading: false, error: null, }), withMethods((store, http = inject(HttpClient)) => ({ async loadData(): Promise { patchState(store, { isLoading: true, error: null }); try { const data = await http.get('/api/data').toPromise(); patchState(store, { data: data || [], isLoading: false }); } catch (error: any) { patchState(store, { error: error.message, isLoading: false, }); } }, })) ); ``` ``` -------------------------------- ### Search Store with RxJS-Based Async Method Source: https://github.com/binsondev/ngrxsignalstore2.0_docs/blob/main/_autodocs/api-reference/with-methods.md Demonstrates using rxMethod to create an observable-based method for search functionality. It includes debouncing, distinctUntilChanged, and handling loading states. ```typescript import { signalStore, withState, withMethods } from '@ngrx/signals'; import { patchState } from '@ngrx/signals'; import { rxMethod } from '@ngrx/signals/rxjs-interop'; import { debounceTime, distinctUntilChanged, pipe, switchMap, tap } from 'rxjs'; export const SearchStore = signalStore( withState({ query: '', results: [], isLoading: false }), withMethods((store) => ({ search: rxMethod( pipe( debounceTime(300), distinctUntilChanged(), tap(() => patchState(store, { isLoading: true })), switchMap((query) => { // Handle search logic return of({ query, results: [] }); }) ) ), })) ); ```