Build a Simple Signals Store Service (Cart Example)
Create a lightweight store using Signals that multiple components can share, without adopting a full framework.
A Signals store is often just a service that holds signals and exposes safe methods. ## Step 1: Create a CartStore service ```ts import { Injectable, computed, signal } from '@angular/core'; export type CartItem = { id: number; title: string; price: number; qty: number }; @Injectable({ providedIn: 'root' }) export class CartStore { private itemsSig = signal<CartItem[]>([]); items = computed(() => this.itemsSig()); count = computed(() => this.itemsSig().reduce((sum, i) => sum + i.qty, 0)); total = computed(() => this.itemsSig().reduce((sum, i) => sum + i.price * i.qty, 0)); add(item: Omit<CartItem, 'qty'>) { this.itemsSig.update(list => { const found = list.find(x => x.id === item.id); if (found) { return list.map(x => (x.id === item.id ? { ...x, qty: x.qty + 1 } : x)); } return [...list, { ...item, qty: 1 }]; }); } remove(id: number) { this.itemsSig.update(list => list.filter(x => x.id !== id)); } clear() { this.itemsSig.set([]); } } ``` ## Step 2: Use it from any component ```ts import { Component } from '@angular/core'; import { CartStore } from './cart.store'; @Component({ selector: 'app-cart-summary', standalone: true, template: ` <p>Items: {{ cart.count() }}</p> <p>Total: ${{ cart.total() }}</p> `, }) export class CartSummaryComponent { constructor(public cart: CartStore) {} } ``` ## Why this pattern is powerful - no boilerplate actions/reducers - easy to read - computed values stay consistent > Next: NgRx explained simply and when it’s worth it.