DEV Community

Kalyan P C
Kalyan P C

Posted on

Writable signals in Angular(v21)

A Writable Signal is the foundation of the new reactive system. While computed signals are for reading and effects are for reacting, Writable Signals are where your data actually lives and breathes.

Think of a Writable Signal as a smart container. You don't just put a value in it; you give the container the ability to broadcast its status to the rest of the app.

1. Creating a Writable Signal

You initialize a signal by calling the signal() function with an initial value. This can be a primitive (string, number, boolean), an array, or a complex object.

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

// A simple counter
const count = signal(7);

// A complex object
const user = signal({ id: 1, name: 'Alex' });
Enter fullscreen mode Exit fullscreen mode

To use the signal in HTML template we need to call the function(as signal is a function that returns a function)

<p>Count: {{count()}}</p>
Enter fullscreen mode Exit fullscreen mode

2. Methods

.set(newValue)

  • This is the "sledgehammer" approach. It replaces the current value with an entirely new one. It doesn't care what the old value was.

  • Example: count.set(10);

.update(fn)

  • This is the most common way to change a signal. You provide a function that takes the current value and returns a new value.

  • Best for: Mathematical operations or appending to strings.

  • Example: count.update(current => current + 1);

.asReadonly

  • Sometimes you want a component to own a signal but prevent other components from changing it. You can expose a read-only version of your writable signal.

  • Effect: It returns a version of the signal that lacks the .set() and .update() methods.

  • Example: public readonlyCount = this.count.asReadonly();

3. Handling Objects and Arrays

Because Angular Signals rely on referential integrity to know when to trigger updates, how you update objects matters.

Updating Objects
If you change a property inside an object without changing the object reference, the signal might not realize it needs to notify consumers. The standard practice is to use the spread operator:

// Changing a username
user.update(currentValue => ({ 
  ...currentValue, 
  name: 'Sam' 
}));
Enter fullscreen mode Exit fullscreen mode

Updating Arrays
Similarly, for arrays, you should return a new array reference rather than just pushing into the old one:

const list = signal(['Item 1']);

// Correct way (creates a new array)
list.update(items => [...items, 'Item 2']);
Enter fullscreen mode Exit fullscreen mode

4. Equality Functions (The Secret Power)

By default, signals use a "strict equality" check (===) to decide if they should notify their followers. However, you can give a Writable Signal a custom "brain" to decide what counts as a change.

If you are working with data where === isn't enough (like complex nested objects), you can pass a custom equality function during initialization:

const mySignal = signal({ id: 1 }, {
  equal: (a, b) => a.id === b.id // Only notifies if the ID actually changed
});
Enter fullscreen mode Exit fullscreen mode

Code Playground

In the latest versions of Angular, you can use Signals with two-way data binding. This is incredibly useful for forms. When a user types into an input, the Writable Signal updates automatically.

<input [(ngModel)]="username" />

<p>Hello, {{ username() }}!</p>
Enter fullscreen mode Exit fullscreen mode

This should help you get started working with signals. Thanks for reading. Happy coding!!!.

Top comments (0)