#angular #signals #state-management #store

Day 7 β€” Angular Signals: writable(), computed(), effect(), Stores & Real App Implementation

πŸ“˜ Day 7 β€” Angular Signals: writable(), computed(), effect(), Stores & Real App Implementation

Zero to Hero β€” Hands-on Angular Tutorial

Today you’ll learn:

  • βœ”οΈ Why Angular created Signals
  • βœ”οΈ writable(), computed(), effect()
  • βœ”οΈ How change detection works with signals
  • βœ”οΈ Deriving reactive state
  • βœ”οΈ Signal-based services (stores)
  • βœ”οΈ Signal forms & API integration
  • βœ”οΈ A real-world Cart Store (like Amazon)
  • βœ”οΈ How signals replace many RxJS use cases
  • βœ”οΈ Best practices (enterprise level)

This day changes how you build Angular apps forever. πŸš€


🟦 1. What Are Signals?

A signal is a reactive variable that:

  • stores a value
  • notifies the UI when it changes
  • needs no async pipe
  • has no subscriptions
  • has automatic cleanup
  • is extremely fast

Example:

import { signal } from '@angular/core';

count = signal(0);

increase() {
  this.count.update(v => v + 1);
}

The template updates instantly:

<p>{{ count() }}</p>
  • βœ”οΈ No change detection issues
  • βœ”οΈ No RxJS complexity
  • βœ”οΈ No memory leaks
  • βœ”οΈ Native Angular reactivity

🟩 2. writable() Signals (Basic State)

count = signal(0);

increment() {
  this.count.update(c => c + 1);
}

reset() {
  this.count.set(0);
}

Template:

<p>Count: {{ count() }}</p>
<button (click)="increment()">+</button>
<button (click)="reset()">Reset</button>
  • βœ”οΈ Simple
  • βœ”οΈ Reactive
  • βœ”οΈ Perfect for UI state

🟧 3. computed() Signals (Derived State)

price = signal(100);
quantity = signal(3);

total = computed(() => this.price() * this.quantity());

Template:

<p>Total: {{ total() }}</p>
  • βœ”οΈ No manual recalculation
  • βœ”οΈ No subscriptions
  • βœ”οΈ Auto-updates anytime data changes

πŸŸ₯ 4. effect() Signals (Side Effects)

Effects run automatically whenever dependencies update.

effect(() => {
  console.log("Total changed:", this.total());
});
  • βœ”οΈ Perfect for saving to localStorage
  • βœ”οΈ Ideal for logging
  • βœ”οΈ Great for analytics
  • βœ”οΈ Should NOT update signals (infinite loop risk)

🟫 5. Build Your First Signal-Based Store

Angular developers use Services + Signals to create scalable stores.

Let’s build a real Cart Store.

Generate the service:

ng g service stores/cart

Open: cart.service.ts

import { Injectable, signal, computed, effect } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class CartService {
  items = signal<{ id: number; name: string; price: number; qty: number }[]>([]);

  // Derived state
  total = computed(() =>
    this.items().reduce((sum, item) => sum + item.price * item.qty, 0)
  );

  count = computed(() =>
    this.items().reduce((sum, item) => sum + item.qty, 0)
  );

  constructor() {
    // Persist cart in localStorage
    effect(() => {
      localStorage.setItem('cart', JSON.stringify(this.items()));
    });
  }

  addItem(product: any) {
    const exists = this.items().find(i => i.id === product.id);

    if (exists) {
      this.items.update(list =>
        list.map(i => i.id === product.id ? { ...i, qty: i.qty + 1 } : i)
      );
    } else {
      this.items.update(list => [...list, { ...product, qty: 1 }]);
    }
  }

  removeItem(id: number) {
    this.items.update(list => list.filter(i => i.id !== id));
  }

  clear() {
    this.items.set([]);
  }
}
  • βœ”οΈ Full reactive store
  • βœ”οΈ State persistence
  • βœ”οΈ Derived values
  • βœ”οΈ Clean, readable API

This is enterprise-level state management β€” without NgRx.


🟦 6. Show Cart in a Component

Generate:

ng g component pages/cart --standalone

cart.component.ts

import { Component, inject } from '@angular/core';
import { CartService } from '../../stores/cart.service';
import { NgFor } from '@angular/common';

@Component({
  selector: 'app-cart',
  standalone: true,
  imports: [NgFor],
  templateUrl: './cart.component.html',
})
export class CartComponent {
  cart = inject(CartService);
}

cart.component.html

<h2>Your Cart</h2>

<ul>
  <li *ngFor="let item of cart.items()">
    {{ item.name }} β€” ${{ item.price }} Γ— {{ item.qty }}
    <button (click)="cart.removeItem(item.id)">Remove</button>
  </li>
</ul>

<p>Total Items: {{ cart.count() }}</p>
<p>Total Price: ${{ cart.total() }}</p>

<button (click)="cart.clear()">Clear Cart</button>
  • βœ”οΈ Fully reactive
  • βœ”οΈ Zero subscriptions
  • βœ”οΈ Zero async pipes
  • βœ”οΈ Ultra-fast

🟧 7. Add Products Page & Add to Cart Feature

Generate:

ng g component pages/products --standalone

products.component.ts

import { Component, inject } from '@angular/core';
import { CartService } from '../../stores/cart.service';
import { NgFor } from '@angular/common';

@Component({
  selector: 'app-products',
  standalone: true,
  imports: [NgFor],
  templateUrl: './products.component.html',
})
export class ProductsComponent {
  cart = inject(CartService);

  products = [
    { id: 1, name: 'Laptop', price: 1200 },
    { id: 2, name: 'Mouse', price: 25 },
    { id: 3, name: 'Keyboard', price: 75 },
  ];
}

products.component.html

<h2>Products</h2>

<div *ngFor="let p of products">
  <p>{{ p.name }} β€” ${{ p.price }}</p>
  <button (click)="cart.addItem(p)">Add to Cart</button>
</div>
  • βœ”οΈ You now have a fully functional shopping cart
  • βœ”οΈ 100% signal-based
  • βœ”οΈ No NgRx or RxJS needed

πŸŸ₯ 8. Why Signals Are Game-Changing

  • βœ”οΈ No Observables needed for most UI state
  • βœ”οΈ No async pipe
  • βœ”οΈ No manual unsubscribe
  • βœ”οΈ Faster performance
  • βœ”οΈ Less boilerplate
  • βœ”οΈ Simpler debugging
  • βœ”οΈ Reactive without RxJS complexity

This is why Angular 17+ defaults to Signals.


🟦 9. When NOT to use Signals

Use RxJS instead when:

  • you need WebSockets
  • you need complex async streams
  • you need debounce / throttle
  • you combine multiple streams
  • the source itself is observable (e.g., events)

Use Signals when:

  • managing UI state
  • storing app-wide state
  • memoizing computed values
  • building dashboards
  • building form state
  • caching responses

πŸŽ‰ End of Day 7 β€” What You Learned

Today you mastered:

  • βœ”οΈ writable(), computed(), effect()
  • βœ”οΈ Signals vs RxJS
  • βœ”οΈ Signal-based services (stores)
  • βœ”οΈ LocalStorage persistence
  • βœ”οΈ Derived state (total, count)
  • βœ”οΈ Real-world shopping cart
  • βœ”οΈ Product + cart components
  • βœ”οΈ Enterprise Angular patterns

Signals are the future β€” and now you know them deeply. πŸ’₯


πŸ§ͺ Day 7 Challenge

Build a Todo Store using only signals.

Features:

  1. Add todo
  2. Toggle complete
  3. Delete todo
  4. Total completed
  5. Total remaining
  6. Load/save from localStorage
  7. Computed signals for metrics
  8. Effect to autosave