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()
Example usage:
export class TodosComponent {
store = inject(TodosStore);
loadTodos() {
this.store.setLoading(); // callState = 'loading'
try {
// ... fetch todos
this.store.setLoaded(); // callState = 'loaded'
} catch (error) {
this.store.setError(error); // callState = { error: 'error message' }
}
}
}
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 } from '@angular-architects/ngrx-toolkit';
const store = signalStore(
withCallState(),
withMethods((store) => ({
async loadData() {
store.setLoading();
try {
// ... async operation
store.setLoaded();
} catch (error) {
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() {
store.todosSetLoading();
try {
// ... load todos
store.todosSetLoaded();
} catch (error) {
store.todosSetError(error);
}
}
}))
);
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() {
store.todosSetLoading();
store.usersSetLoading();
// ... load data for both collections
}
}))
);
Helper Methods
setLoading()
Sets the call state to 'loading':
store.setLoading(); // Basic usage
store.setLoading('todos'); // With collection
setLoaded()
Sets the call state to 'loaded':
store.setLoaded(); // Basic usage
store.setLoaded('todos'); // With collection
setError()
Sets the call state to error with a message:
store.setError(error); // Basic usage
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>
}
`
})