Services are an excellent way for components to communicate - like messengers between castles.
1import { Injectable, signal, computed } from '@angular/core';
2
3export interface CartItem {
4 id: number;
5 name: string;
6 quantity: number;
7 price: number;
8}
9
10@Injectable({ providedIn: 'root' })
11export class CartService {
12 // State as signals
13 private items = signal<CartItem[]>([]);
14
15 // Computed values
16 readonly cartItems = this.items.asReadonly();
17 readonly totalItems = computed(() =>
18 this.items().reduce((sum, item) => sum + item.quantity, 0)
19 );
20 readonly totalPrice = computed(() =>
21 this.items().reduce((sum, item) => sum + item.price * item.quantity, 0)
22 );
23
24 addItem(item: Omit<CartItem, 'quantity'>): void {
25 const existing = this.items().find(i => i.id === item.id);
26 if (existing) {
27 this.items.update(items =>
28 items.map(i => i.id === item.id
29 ? { ...i, quantity: i.quantity + 1 }
30 : i
31 )
32 );
33 } else {
34 this.items.update(items => [...items, { ...item, quantity: 1 }]);
35 }
36 }
37
38 removeItem(id: number): void {
39 this.items.update(items => items.filter(i => i.id !== id));
40 }
41
42 clearCart(): void {
43 this.items.set([]);
44 }
45}1// header.component.ts - displays the count
2@Component({
3 template: \`<span>🛒 {{ cartService.totalItems() }}</span>\`
4})
5export class HeaderComponent {
6 cartService = inject(CartService);
7}
8
9// product.component.ts - adds to cart
10@Component({
11 template: \`<button (click)="addToCart()">Add</button>\`
12})
13export class ProductComponent {
14 private cartService = inject(CartService);
15
16 addToCart(): void {
17 this.cartService.addItem({
18 id: this.product.id,
19 name: this.product.name,
20 price: this.product.price
21 });
22 }
23}