We use cookies to enhance your experience on the site
CodeWorlds

State Management - The Shogun's Treasury

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.

What is Application State?

State is all the data that an application stores:

  • UI State - whether a modal is open, the active tab
  • Server State - data from API (users, products)
  • Form State - form values and validation
  • Router State - current URL path

Simple State Management with Signals

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}
Go to CodeWorlds