Managing state is like running the shogun's treasury - it requires order, control, and precise accounting of every resource. In feudal Japan, the shogun had to know the state of his resources at all times.
State is all the data that an application stores:
1import { Injectable, signal, computed, inject } from '@angular/core';
2import { HttpClient } from '@angular/common/http';
3
4interface SamuraiState {
5 samurai: Samurai[];
6 selectedId: number | null;
7 loading: boolean;
8 error: string | null;
9}
10
11@Injectable({ providedIn: 'root' })
12export class SamuraiStore {
13 private http = inject(HttpClient);
14
15 // Private state signals
16 private state = signal<SamuraiState>({
17 samurai: [],
18 selectedId: null,
19 loading: false,
20 error: null
21 });
22
23 // Public selectors (readonly)
24 readonly samurai = computed(() => this.state().samurai);
25 readonly selectedId = computed(() => this.state().selectedId);
26 readonly loading = computed(() => this.state().loading);
27 readonly error = computed(() => this.state().error);
28
29 // Derived selectors
30 readonly selectedSamurai = computed(() => {
31 const id = this.selectedId();
32 return id ? this.samurai().find(s => s.id === id) : null;
33 });
34
35 readonly samuraiCount = computed(() => this.samurai().length);
36
37 readonly samuraiByClan = computed(() => {
38 const grouped = new Map<string, Samurai[]>();
39 for (const s of this.samurai()) {
40 const list = grouped.get(s.clan) || [];
41 list.push(s);
42 grouped.set(s.clan, list);
43 }
44 return grouped;
45 });
46
47 // Actions (state mutations)
48 loadSamurai(): void {
49 this.state.update(s => ({ ...s, loading: true, error: null }));
50
51 this.http.get<Samurai[]>('/api/samurai').subscribe({
52 next: (samurai) => {
53 this.state.update(s => ({ ...s, samurai, loading: false }));
54 },
55 error: (err) => {
56 this.state.update(s => ({
57 ...s,
58 loading: false,
59 error: err.message
60 }));
61 }
62 });
63 }
64
65 selectSamurai(id: number | null): void {
66 this.state.update(s => ({ ...s, selectedId: id }));
67 }
68
69 addSamurai(samurai: Samurai): void {
70 this.state.update(s => ({
71 ...s,
72 samurai: [...s.samurai, samurai]
73 }));
74 }
75
76 removeSamurai(id: number): void {
77 this.state.update(s => ({
78 ...s,
79 samurai: s.samurai.filter(sam => sam.id !== id)
80 }));
81 }
82}