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