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
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
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
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