withCallState()
import { withCallState } from '@angular-architects/ngrx-toolkit';
withCallState
adds call state management capabilities to NgRx signal stores, tracking the status of asynchronous operations with built-in states for loading, loaded, and error conditions.
Basic Usage
The simplest way to use withCallState
is without any configuration:
export const TodosStore = signalStore(
withCallState()
// ... other features
);
This provides you with:
- A
callState
state property of type'init' | 'loading' | 'loaded' | { error: string }
- Computed signals:
loading
,loaded
, anderror
- Helper methods:
setLoading()
,setLoaded()
, andsetError()
Use Cases
- Track loading states for async operations
- Handle error states consistently
- Manage multiple named collections of call states
Type Constraints
The call state can be one of these types:
'init'
- Initial state'loading'
- Operation in progress'loaded'
- Operation completed successfully{ error: string }
- Operation failed with error
Usage
import { withCallState, setLoading, setLoaded, setError } from '@angular-architects/ngrx-toolkit';
const store = signalStore(
withCallState(),
withMethods((store) => ({
async loadData() {
patchState(store, setLoading());
try {
// ... async operation
patchState(store, setLoaded());
} catch (error) {
patchState(store, setError(error));
}
},
}))
);
Named Collection
You can track state for a specific collection by providing a collection name:
export const TodosStore = signalStore(
withCallState({ collection: 'todos' })
// ... other features
);
This provides:
- A
todosCallState
state property - Computed signals:
todosLoading
,todosLoaded
, andtodosError
- Helper methods with optional collection parameter
const store = signalStore(
withCallState({ collection: 'todos' }),
withMethods((store) => ({
async loadTodos() {
patchState(store, setLoading('todos'));
try {
// ... load todos
patchState(store, setLoaded('todos'));
} catch (error) {
patchState(store, setError(error, 'todos'));
}
},
}))
);
Multiple Collections
For managing multiple async operations, use the collections configuration:
export const TodosStore = signalStore(
withCallState({ collections: ['todos', 'categories'] })
// ... other features
);
This creates separate states and signals for each collection:
- States:
todosCallState
,categoriesCallState
- Signals:
todosLoading
,todosLoaded
,todosError
,categoriesLoading
, etc.
const store = signalStore(
withCallState({ collections: ['todos', 'users'] }),
withMethods((store) => ({
async loadAll() {
patchState(store, setLoading('todos'), setLoading('users'));
// ... load data for both collections
},
}))
);
Helper Methods
setLoading()
Sets the call state to 'loading':
patchState(store, setLoading()); // Basic usage
patchState(store, setLoading('todos')); // With collection
setLoaded()
Sets the call state to 'loaded':
patchState(store, setLoaded()); // Basic usage
patchState(store, setLoaded('todos')); // With collection
setError()
Sets the call state to error with a message:
patchState(store, setError(error)); // Basic usage
patchState(store, setError(error, 'todos')); // With collection
Accessing State
Access the computed signals in your templates or component code:
@Component({
template: `
@if (store.loading()) {
<div>Loading...</div>
} @if (store.error()) {
<div>Error: {{ store.error() }}</div>
} @if (store.loaded()) {
<div>Content loaded!</div>
}
`,
})
export class MyComponent {
store = inject(MyStore);
}
For collections:
@Component({
template: `
@if (store.todosLoading()) {
<div>Loading todos...</div>
}
@if (store.categoriesLoading()) {
<div>Loading categories...</div>
}
`
})