Skip to main content
Practice Problems

Signals in Angular

What are Signals?

Signals (introduced in Angular 16) are a new reactive primitive for managing state. They provide fine-grained reactivity β€” Angular knows exactly what changed and updates only the affected parts of the UI.


Basic Usage

typescript
import { signal, computed, effect } from '@angular/core'; @Component({ template: \` <p>Count: {{ count() }}</p> <p>Double: {{ doubleCount() }}</p> <button (click)="increment()">+</button> <button (click)="reset()">Reset</button> \` }) export class CounterComponent { // Writable signal count = signal(0); // Computed signal (derived, read-only) doubleCount = computed(() => this.count() * 2); increment() { // Set new value this.count.set(this.count() + 1); // Or update based on previous value this.count.update(c => c + 1); } reset() { this.count.set(0); } }

signal() β€” Writable Signal

typescript
const name = signal('Alice'); // Read console.log(name()); // "Alice" // Write name.set('Bob'); // Update based on current value name.update(current => current.toUpperCase()); // Mutate (for objects/arrays β€” mutates in place) const users = signal([{ name: 'Alice' }]); users.mutate(arr => arr.push({ name: 'Bob' }));

computed() β€” Derived Signal

typescript
const firstName = signal('John'); const lastName = signal('Doe'); // Automatically recalculates when dependencies change const fullName = computed(() => \`\${firstName()} \${lastName()}\`); console.log(fullName()); // "John Doe" firstName.set('Jane'); console.log(fullName()); // "Jane Doe" // computed signals are READ-ONLY // fullName.set('X'); // ❌ Error!

effect() β€” Side Effects

typescript
const count = signal(0); // Runs whenever count changes effect(() => { console.log('Count changed to:', count()); // Log, save to localStorage, API call, etc. }); count.set(5); // Logs: "Count changed to: 5"

Signals vs Observables

FeatureSignalsObservables (RxJS)
Synchronousβœ… Always has value❌ May be async
Current valueβœ… signal()⚠️ Need BehaviorSubject
Auto-cleanupβœ… No subscriptions❌ Must unsubscribe
Template usage{{ signal() }}{{ obs$ | async }}
Operators❌ Noneβœ… Rich operators
Streams/Events❌ Not designed forβœ… Built for streams
Change detectionFine-grainedZone-based

Input Signals (Angular 17+)

typescript
@Component({ /* ... */ }) export class UserCard { // Signal-based inputs name = input.required<string>(); age = input(0); // With default value role = input<'admin' | 'user'>('user'); // Computed from input signals isAdult = computed(() => this.age() >= 18); }
html
<app-user-card [name]="'Alice'" [age]="25" />

Practical Example: Shopping Cart

typescript
@Component({ /* ... */ }) export class CartComponent { items = signal<CartItem[]>([]); totalPrice = computed(() => this.items().reduce((sum, item) => sum + item.price * item.quantity, 0) ); itemCount = computed(() => this.items().reduce((sum, item) => sum + item.quantity, 0) ); addItem(item: CartItem) { this.items.update(items => [...items, item]); } removeItem(id: string) { this.items.update(items => items.filter(i => i.id !== id)); } }

Important:

Signals are Angular's modern reactivity system β€” simpler than RxJS for synchronous state. Use signal() for state, computed() for derived values, and effect() for side effects. They enable fine-grained change detection (no Zone.js needed). Keep using RxJS for async streams (HTTP, WebSocket, events). Signals and Observables work together.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems