Signal Store live
Ein vollständiges, kommentiertes Signal-Store-Beispiel aus der Tasks-Domäne.
Store-Definition
Abschnitt betitelt „Store-Definition“import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';import { computed, inject } from '@angular/core';import { TasksService } from '../infrastructure/tasks.service';
type TasksState = { tasks: Task[]; selectedTaskId: string | null; status: 'idle' | 'loading' | 'success' | 'error';};
const initialState: TasksState = { tasks: [], selectedTaskId: null, status: 'idle',};
export const TasksStore = signalStore( { providedIn: 'root' }, withState(initialState),
withComputed(({ tasks, selectedTaskId }) => ({ selectedTask: computed(() => tasks().find((t) => t.id === selectedTaskId()) ?? null), openTasksCount: computed(() => tasks().filter((t) => t.status === 'open').length), })),
withMethods((store) => { const service = inject(TasksService); return { async loadAll() { patchState(store, { status: 'loading' }); try { const tasks = await service.getAll(); patchState(store, { tasks, status: 'success' }); } catch { patchState(store, { status: 'error' }); } }, selectTask(id: string) { patchState(store, { selectedTaskId: id }); }, }; }),);Facade davor
Abschnitt betitelt „Facade davor“@Injectable({ providedIn: 'root' })export class TasksFacade { private store = inject(TasksStore);
readonly tasks = this.store.tasks; readonly selectedTask = this.store.selectedTask; readonly isLoading = computed(() => this.store.status() === 'loading');
loadAll() { this.store.loadAll(); } selectTask(id: string) { this.store.selectTask(id); }}Komponente
Abschnitt betitelt „Komponente“@Component({ template: ` @if (facade.isLoading()) { <p>Lädt…</p> } <app-task-list [tasks]="facade.tasks()" (select)="facade.selectTask($event)" /> `,})export class TasksContainerComponent { protected facade = inject(TasksFacade);}Store kapselt Zustand. Facade kapselt Store. Komponente kennt nur die Facade.