Signals Basics (v16)
📚 What are Signals?
Signals are Angular's reactive primitive for state management:
- Reactive: UI updates automatically when signal value changes
- Fine-grained: Only affected parts of UI update
- Synchronous: Values are read synchronously (not like observables)
- Simple API: signal(), get(), set(), update()
- No subscriptions: No memory leaks from forgotten unsubscribes
📅 Signals Version History
v16Signals introduced (Developer Preview)
v17signal(), computed(), effect() stable
v18input(), output(), model() stable
v19resource(), linkedSignal() added
🎯 Interview Questions
- Q1: What is the difference between signals and observables?
- A: Signals are synchronous and always have a value. Observables are async streams. Signals don't need subscriptions/unsubscriptions.
- Q2: How do you read a signal value?
- A: Call the signal as a function: mySignal(). In templates: {{ mySignal() }}
- Q3: What is the difference between set() and update()?
- A: set(newValue) replaces entirely. update(fn) takes previous value: update(prev => prev + 1)
- Q4: How do signals improve performance?
- A: Fine-grained reactivity means only components reading changed signals update, not entire component tree.
🔧 Live Demo - Signal Operations
Basic Counter Signal
0
Object Signal
Name: John
Age: 30
Array Signal
- Apple
- Banana
- Cherry
Read-only Signal (asReadonly)
Read-only count: 0
asReadonly() creates a read-only view - useful for exposing state publicly
📋 Signal API Reference
| Method | Description | Example |
|---|---|---|
| signal() | Create writable signal | signal(0) |
| mySignal() | Read signal value | count() |
| .set(value) | Set new value | count.set(10) |
| .update(fn) | Update based on previous | count.update(c => c + 1) |
| .asReadonly() | Create read-only view | count.asReadonly() |
💻 Signals Code
import { signal } from '@angular/core';
// ===== Creating Signals =====
// Primitive signal
const count = signal(0);
// Object signal
const user = signal({ name: 'John', age: 30 });
// Array signal
const items = signal<string[]>([]);
// ===== Reading Signals =====
// Call signal as function to read
const currentCount = count(); // 0
const userName = user().name; // 'John'
// In template
// {{ count() }}
// {{ user().name }}
// ===== Updating Signals =====
// set() - replace value entirely
count.set(10);
user.set({ name: 'Jane', age: 25 });
// update() - based on previous value
count.update(c => c + 1);
user.update(u => ({ ...u, age: u.age + 1 }));
items.update(arr => [...arr, 'New Item']);
// ===== Read-only Signals =====
// Expose read-only version publicly
private readonly _count = signal(0);
readonly count = this._count.asReadonly();
// Outside code can read but not modify
console.log(this.count()); // ✅ Works
// this.count.set(5); // ❌ Error - no set method
// ===== Signal Options =====
// Custom equality function
const obj = signal({ a: 1 }, {
equal: (prev, curr) => prev.a === curr.a
});
// ===== Signals vs BehaviorSubject =====
// BehaviorSubject (RxJS)
const subject = new BehaviorSubject(0);
subject.subscribe(v => console.log(v)); // Need subscription
subject.next(1);
subject.unsubscribe(); // Need cleanup
// Signal (Angular)
const sig = signal(0);
console.log(sig()); // Just read, no subscription!
sig.set(1);
// No cleanup needed!