Chapter 10.1โ˜• 15 min read

Writable Signals โ€” signal() and .set() .update()

Signals are Angular's reactive primitive for state. A signal wraps a value and notifies Angular when it changes โ€” enabling fine-grained reactivity without Zone.js.

01What are Signals

Signals are Angular's built-in reactive primitive for state management, introduced in Angular 16.

A signal is a wrapper around a value that notifies consumers when the value changes. Think of it as a "reactive variable" โ€” when you change it, everything that reads it automatically knows.

"Signal = LED display board โ€” value badlo toh sabko pata chal jayega."

Why signals matter:

  • Replaces BehaviorSubject for most component/service state
  • Replaces ngOnChanges for detecting input changes (future)
  • Replaces Zone.js โ€” no more automatic change detection scanning
  • NOT RxJS โ€” no operators, no subscribe, no unsubscribe
  • Signals are synchronous โ€” value is available immediately
// Old way: BehaviorSubject + subscribe + unsubscribe
private count$ = new BehaviorSubject(0);
count$.subscribe(v => this.count = v);

// New way: signal โ€” just a reactive variable
count = signal(0);

// Read anywhere, even in template:
// {{ count() }}

Key concept: Signals are functions. You call them to READ the value. You call .set() or .update() to WRITE the value.

02Creating a Signal

Creating a signal is simple โ€” call signal() with an initial value.

import { signal } from '@angular/core';

count = signal(0);                    // number
name = signal('Hyderabadi Biryani'); // string
isReady = signal(false);             // boolean
biryanis = signal<Biryani[]>([]);   // array
user = signal<User | null>(null);   // union type

"signal() = signal banao โ€” initial value do, TypeScript type automatically infer karega."

TypeScript inference:

  • signal(0) โ†’ Signal<number>
  • signal('hello') โ†’ Signal<string>
  • signal({ name: 'Biryani' }) โ†’ Signal<{ name: string }>
  • For complex types, use generic: signal<Biryani[]>([])

Location matters โ€” where to declare signals:

  • Inside components: at class field level (preferred) or in constructor
  • Inside services: for shared state
  • Inside directives: for UI state
@Component({...})
export class BiryaniComponent {
  // Best practice: declare as class fields
  biryaniName = signal('Hyderabadi Biryani');
  price = signal(250);
  rating = signal(4.5);
}
03Reading and Writing Signals

Reading a signal is like calling a function โ€” just use parentheses.

const count = signal(0);

// READ โ€” call the signal like a function
console.log(count()); // 0

Writing uses .set() or .update():

// .set() โ€” replace the entire value
count.set(10);  // count is now 10

// .update() โ€” transform based on previous value
count.update(val => val + 1);  // count is now 11 (10 + 1)

// .set() for simple replacement
name.set('Chicken Biryani');

// .update() for increment/toggle/transform
isOpen.update(val => !val);      // toggle boolean
indices.update(arr => [...arr, newId]); // add to array

.set() vs .update():

  • .set() = purana plate hata ke naya rakh do โ€” complete replacement
  • .update() = purane mein kuch add karo โ€” transform based on previous

โš ๏ธ Important: For objects/arrays, you must create a NEW reference for change detection to fire:

items = signal(['Biryani', 'Kebabs']);

// โŒ WRONG โ€” mutating the same array
items().push('Curry');  // No change detection!

// โœ… RIGHT โ€” create new array
items.update(arr => [...arr, 'Curry']);
04Signals in Templates

Signals in templates are just function calls โ€” use () just like in TypeScript.

@Component({
  selector: 'app-biryani',
  template: `
    <h1>{{ biryaniName() }}</h1>
    <p>Price: โ‚น{{ price() }}</p>

    @if (isAvailable()) {
      <button (click)="order()">Order Now</button>
    } @else {
      <p class="sold-out">Sold Out!</p>
    }

    <div class="items">
      @for (item of menu(); track item.id) {
        <span>{{ item.name }}</span>
      }
    </div>
  `
})
export class BiryaniComponent {
  biryaniName = signal('Hyderabadi Biryani');
  price = signal(250);
  isAvailable = signal(true);
  menu = signal<MenuItem[]>([]);
}

Angular tracks which signals are read in the template. When a signal changes, ONLY the parts of the template that read that signal re-render โ€” not the whole component!

"Template mein bhi () lagao โ€” signal hai function ki tarah. Angular track karta hai kaunsa signal kahan read ho raha hai."

This is called fine-grained reactivity โ€” instead of Zone.js scanning the whole component, Angular knows exactly which DOM nodes to update, on what signal change.

05Why Signals are Better Than Properties

Why are signals better than plain class properties?

๐Ÿ’ก Signal vs Property โ€” The Key Difference

PropertySignal
this.count = 5this.count.set(5)
Angular doesn't know it changedAngular KNOWS it changed
Need ChangeDetectorRefNo ChangeDetectorRef needed
Zone.js scans entire treeOnly consumers of that signal update
Template: {{ count }}Template: {{ count() }}

"Property = notice board jo koi nahi dekhta. Signal = WhatsApp group โ€” change hua toh notification sabko milta hai."

Benefits in real projects:

  • No more ChangeDetectorRef.detectChanges() hacks
  • No more markForCheck() calls
  • Better performance โ€” Angular only re-renders what changed
  • Future: Zone.js becomes optional โ€” signals handle change detection

Zone.js vs Signals future: In Angular 18+, you can disable Zone.js with provideExperimentalZonelessChangeDetection(). Signals become essential โ€” without Zone.js, only signal changes trigger UI updates. This makes apps faster (no zone pollution) and gives you predictable change detection.

Key Takeaways

  • โœ… Signal = reactive variable โ€” wraps a value, notifies consumers on change
  • โœ… Create: const x = signal(initialValue) โ€” TypeScript infers type
  • โœ… Read: x() โ€” call the signal as a function
  • โœ… Write .set(): x.set(newValue) โ€” complete replacement
  • โœ… Write .update(): x.update(prev => prev + 1) โ€” transform based on previous
  • โœ… Templates: use () โ€” {{ x() }}, @if (x()), @for of x()
  • โœ… Always create NEW reference for objects/arrays โ€” no mutation!
Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase