liveBook · Manning

WritableSignal<T>

As explained in Pro Angular 16

A writable signal is an Angular signal whose value can be changed directly through writable operations.

A WritableSignal<T> is an Angular signal whose value represents mutable application state. Create one with signal<T>(initialValue), read it by invoking it as a function, and change it through writable operations rather than assignment. section 10.4.1

Choose the operation that matches the kind of change:

  • set(value) replaces the signal’s current value. section 10.4.1
  • update(function) receives the current value and returns a new value. section 10.4.1
  • mutate(function) receives the current value and modifies it in place, making it useful for incremental array or object changes. section 6.2.1 section 10.4.1
  • asReadonly() returns a Signal<T> that can be read and used by computed signals but cannot be modified directly. section 10.4.1

Encapsulating cart state

A Cart service stores its product selections in a private WritableSignal<CartLine[]> initialized with signal([]). Methods such as addLine and removeLine use mutate to apply changes. A lines getter returns linesSignal.asReadonly(), so the Cart controls every modification while components can read the selections and derive summaries with computed. section 6.2.1

Changing a displayed index

private index = signal<number>(0); changes the initial value from a plain number to writable state. A toggle operation can call index.update(currentVal => (currentVal + 1) % 2), while templates read the value with index(). section 10.4.1

Listing 6.1. The contents of the cart.model.ts file in the src/app/model folder
import { Injectable, Signal, WritableSignal, computed, signal } 
    from "@angular/core";
import { Product } from "./product.model";
 
@Injectable()
export class Cart {
    private linesSignal: WritableSignal<CartLine[]>;
    public summary: Signal<CartSummary>;
 
    constructor() {
        this.linesSignal = signal([]);
 
        this.summary = computed(() => {
            let newSummary = new CartSummary();
            this.linesSignal().forEach(l => {
                newSummary.itemCount += l.quantity;
                newSummary.cartPrice += l.lineTotal;
            });
            return newSummary;
        })
    }
 
    get lines(): Signal<CartLine[]> {
        return this.linesSignal.asReadonly();
    }
 
    addLine(product: Product, quantity: number = 1) {
        this.linesSignal.mutate(linesArray => {
            let line = linesArray.find(l => l.product.id == product.id);
            if (line != undefined) {
                line.quantity += quantity;
            } else {
                linesArray.push(new CartLine(product, quantity));
            }
        });
    }
 
    updateQuantity(product: Product, quantity: number) {
        this.linesSignal.mutate(linesArray => {
            let line = linesArray.find(l => l.product.id == product.id);
            if (line != undefined) {
                line.quantity = Number(quantity);
            }
        });
    }
 
    removeLine(id: number) {
        this.linesSignal.mutate(linesArray => {
            let index = linesArray.findIndex(l => l.product.id == id);
            linesArray.splice(index, 1);
        });
    }
 
    clear() {
        this.linesSignal.set([]);
    }
}
 
export class CartLine {
    
    constructor(public product: Product,
        public quantity: number) {}
 
    get lineTotal() {
        return this.quantity * (this.product.price ?? 0);
    }
}
 
export class CartSummary {
    itemCount: number = 0;
    cartPrice: number = 0;
}

Inspect a complete Cart model showing private writable state, mutate-based operations, a read-only lines view, and a computed summary. section 6.2.1

Writable signals describe relationships between mutable values so Angular can update affected bindings without evaluating unrelated template expressions. Use computed signals for derived values, and avoid using effects to modify writable signals or other application state. section 10.6 section 10.4.3

How Pro Angular 16 covers it

How deep it goes
Explains creation, reading, set, update, mutate, and asReadonly, then stops short of broader signal internals.
Examples
Demonstrates a toggled index, product data repository, and shopping cart with incrementally modified selections.
What it assumes
Assumes basic Angular components and TypeScript, while teaching signal creation, invocation, mutation, and read-only exposure.
What you can do after
Build mutable Angular state, update it with named writable methods, and expose controlled read-only views.
How it connects
Connects writable signals with computed signals, effects, change detection, services, dependency injection, and observables.

Also explained in

Compare how 2 books explain WritableSignal<T> →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Pro Angular 16. The section links point to the parts of the book it draws on.