Angular19 min read

Build a Simple Signals Store Service (Cart Example)

Create a lightweight store using Signals that multiple components can share, without adopting a full framework.

Olivia Scott
July 25, 2025
7.7k185

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.
#Angular#Signals#Architecture#Advanced