Ultimate Angular Signals Guide
Modern Fine-Grained Reactivity Reference & Technical Interview Guide

1. Quick Comparison: Signals vs RxJS Observables

Feature Angular Signals RxJS Observables
Primary Purpose Fine-grained local & UI state management Asynchronous streams, events, HTTP, time-based ops
Reading Value Synchronous function call: count() Asynchronous via .subscribe() or async pipe
Memory Cleanup Automatic (No manual unsubscribe required!) Requires manual cleanup (takeUntilDestroyed)
Change Detection Direct signal node notification (Glitch-free) Zone.js dirty checking or ChangeDetectorRef
Data Flow Value-based (Always holds a current value) Stream-based (Can emit 0, 1, or infinite values over time)

2. Core Signal Primitives

2.1 signal() — Writable Signals

Real-Life Scenario: Counter, Toggle State, Form Inputs, Loading Spinners

💡 Why use it: Holds a mutable value and directly triggers UI updates when mutated.
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <h2>Count: {{ count() }}</h2>
    <button (click)="increment()">+1</button>
    <button (click)="reset()">Reset</button>
  `
})
export class CounterComponent {
  count = signal<number>(0);

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

2.2 computed() — Derived Read-Only Signals

Real-Life Scenario: Shopping Cart Subtotal & Grand Total, Filtered List

💡 Why use it: Declarative memoized value that only recalculates when dependent signals change.
import { Component, signal, computed } from '@angular/core';

@Component({ selector: 'app-cart', template: `...` })
export class CartComponent {
  price = signal<number>(100);
  quantity = signal<number>(2);

  subtotal = computed(() => this.price() * this.quantity());
  grandTotal = computed(() => this.subtotal() * 1.10); // 10% tax
}

2.3 effect() — Side-Effect Execution

Real-Life Scenario: Auto-saving state to localStorage, Logging, Chart Sync

💡 Why use it: Automatically re-runs whenever any signal read inside its body updates.
constructor() {
  effect(() => {
    const currentTheme = this.theme();
    localStorage.setItem('theme', currentTheme);
    document.body.className = currentTheme;
  });
}

3. Component I/O Signals (Angular 17+ / 18+)

3.1 input() & input.required()

Real-Life Scenario: Parent to Child Data Passing

export class UserCardComponent {
  userRole = input<string>('Guest'); // Optional input
  userName = input.required<string>(); // Required input
}

3.2 output() (Signal Output Event)

Real-Life Scenario: Child to Parent Event Emission

export class DeleteButtonComponent {
  itemDeleted = output<number>();

  onDelete() { this.itemDeleted.emit(42); }
}

3.3 model() (Two-Way Signal Binding)

Real-Life Scenario: Custom Form Controls / Custom Switches

export class CustomToggleComponent {
  checked = model<boolean>(false); // Writable two-way signal
}

4. RxJS & Signals Interoperability (rxjs-interop)

4.1 toSignal() — Observable to Signal Conversion

Real-Life Scenario: HTTP API Requests to Template Signals

products = toSignal(
  this.http.get<Product[]>('/api/products'),
  { initialValue: [] }
);

4.2 toObservable() — Signal to Observable Conversion

Real-Life Scenario: Piping Signal Value through RxJS Operators (debounceTime, switchMap)

searchResults = toSignal(
  toObservable(this.searchTerm).pipe(
    debounceTime(300),
    switchMap(query => this.http.get(`/api/search?q=${query}`))
  ),
  { initialValue: [] }
);

5. Advanced Signals (Angular 19+)

5.1 linkedSignal()

Real-Life Scenario: Linked Dropdown Options (Country -> State reset)

selectedCountry = signal('USA');

selectedState = linkedSignal({
  source: this.selectedCountry,
  computation: (country) => country === 'USA' ? 'California' : 'Ontario'
});

Post a Comment

Previous Post Next Post