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:
- Add todo
- Toggle complete
- Delete todo
- Total completed
- Total remaining
- Load/save from localStorage
- Computed signals for metrics
- Effect to autosave