# Angular Signals

Course: [Angular](https://www.devtutorials.ai/learn/angular) · Section: Application State
Source: https://www.devtutorials.ai/learn/angular/state/signals

> Storing and updating values with signals: writable signals, reading and updating values, computed signals, effects, signals compared with RxJS, and best practices.

## What are Signals?

A **signal** is a box that stores one value. When the value in the box changes, Angular updates every part of the screen that reads it.

You create a signal with the `signal()` function, and you read its value by calling it like a function: `count()`.

### Real-World Example

On the cart page of an online store, the quantity of each item is stored in a signal. The subtotal, tax and total are calculated from the quantities.

When the user changes a quantity, Angular updates only the numbers that depend on it. The rest of the page stays as it is.

<figure className="ngc-fig">
<svg viewBox="0 0 640 216" role="img" aria-label="Two writable signals, price and qty, feed a computed total. The total is read by the template and by an effect.">
<defs><marker id="ngc-g1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-blue"><rect x="10" y="20" width="150" height="60" rx="9" /><text x="85.0" y="47" textAnchor="middle" style={{fontSize:"14px"}}>price = 50</text><text className="ngc-sub" x="85.0" y="65" textAnchor="middle">writable signal</text></g></g><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-blue"><rect x="10" y="110" width="150" height="60" rx="9" /><text x="85.0" y="137" textAnchor="middle" style={{fontSize:"14px"}}>qty = 3</text><text className="ngc-sub" x="85.0" y="155" textAnchor="middle">writable signal</text></g></g><line className="ngc-link ngc-flow c-slate" x1="162" y1="50" x2="236" y2="80" markerEnd="url(#ngc-g1)" /><line className="ngc-link ngc-flow c-slate" x1="162" y1="140" x2="236" y2="110" markerEnd="url(#ngc-g1)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-solid c-purple"><rect x="238" y="65" width="160" height="60" rx="9" /><text x="318.0" y="92" textAnchor="middle" style={{fontSize:"14px"}}>total = 150</text><text className="ngc-sub" x="318.0" y="110" textAnchor="middle">computed</text></g></g><line className="ngc-link ngc-flow c-slate" x1="400" y1="85" x2="466" y2="50" markerEnd="url(#ngc-g1)" /><line className="ngc-link ngc-flow c-slate" x1="400" y1="105" x2="466" y2="140" markerEnd="url(#ngc-g1)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-green"><rect x="468" y="20" width="160" height="60" rx="9" /><text x="548.0" y="47" textAnchor="middle" style={{fontSize:"14px"}}>Template</text><text className="ngc-sub" x="548.0" y="65" textAnchor="middle">shows Total: 150</text></g></g><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-orange"><rect x="468" y="110" width="160" height="60" rx="9" /><text x="548.0" y="137" textAnchor="middle" style={{fontSize:"14px"}}>effect()</text><text className="ngc-sub" x="548.0" y="155" textAnchor="middle">writes a log line</text></g></g><text className="ngc-note" x="320" y="204" textAnchor="middle">Change qty and Angular recalculates total, then updates the template and runs the effect.</text>
</svg>
<figcaption>Signals pass a change to everything that reads them.</figcaption>
</figure>

## Why Signals?

In earlier lessons, normal fields updated the screen after a click. But a normal field does not update the screen when its value changes later, for example in a timer or after a server answer. A signal always updates the screen, whenever its value changes.

Benefits of signals:

- **The screen always shows the current value**, after a click, a timer or a server answer.
- **Angular knows exactly what to update.** It updates only the parts of the screen that read the changed signal.
- **Values can depend on other values.** A computed signal calculates its value from other signals automatically.
- **Simple code.** You read with `count()` and change with `set()` or `update()`. You do not need `subscribe()`.

| Point | Normal field | Signal |
|---|---|---|
| Create | `count = 0` | `count = signal(0)` |
| Read | `count` | `count()` |
| Change | `count = 5` | `count.set(5)` |
| Screen updates after a timer or server answer | No | Yes |

## Writable Signals

A **writable signal** is a signal whose value you can change. You create it with `signal()` and give it a starting value:

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

quantity = signal(1);           // a number
userName = signal('Guest');     // a string
items = signal<string[]>([]);   // an empty list of strings
```

For an empty list, write the type in `< >`, for example `signal<string[]>([])`. Otherwise TypeScript does not know what the list will contain.

## Reading Signal Values

To read the value, call the signal like a function, with `()`:

- In the template: `{{ quantity() }}`
- In the class: `this.quantity()`

```angular runnable title="Creating and reading signals"
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <p>Product: {{ product() }}</p>
    <p>Price: {{ price() }}</p>
    <p>{{ stockMessage() }}</p>
  `,
})
export class App {
  product = signal('Wireless Mouse');
  price = signal(15);
  stock = signal(0);

  stockMessage() {
    return this.stock() > 0 ? 'In stock' : 'Out of stock';
  }
}
```

The method `stockMessage()` reads the `stock` signal with `this.stock()`.

If you forget the `()`, you get the signal box, not its value. `{{ price }}` shows `[Signal: 15]` on the page, not `15`.

## Updating Signal Values

A writable signal has two methods to change its value:

<figure className="ngc-fig">
<svg viewBox="0 0 640 234" role="img" aria-label="set replaces the value; update calculates the new value from the current value.">
<defs><marker id="ngc-g2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><text className="ngc-title c-blue" x="160" y="22" textAnchor="middle">set(value)</text><text className="ngc-title c-green" x="480" y="22" textAnchor="middle">update(fn)</text><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-blue"><rect x="30" y="36" width="260" height="46" rx="9" /><text x="160.0" y="64" textAnchor="middle" style={{fontSize:"13px"}}>quantity = 3</text></g></g><line className="ngc-link ngc-flow c-slate" x1="160" y1="83" x2="160" y2="99" markerEnd="url(#ngc-g2)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-blue"><rect x="30" y="100" width="260" height="46" rx="9" /><text x="160.0" y="120" textAnchor="middle" style={{fontSize:"13px"}}>quantity.set(1)</text><text className="ngc-sub" x="160.0" y="138" textAnchor="middle">the old value is ignored</text></g></g><line className="ngc-link ngc-flow c-slate" x1="160" y1="147" x2="160" y2="163" markerEnd="url(#ngc-g2)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-blue"><rect x="30" y="164" width="260" height="46" rx="9" /><text x="160.0" y="192" textAnchor="middle" style={{fontSize:"13px"}}>quantity = 1</text></g></g><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-green"><rect x="350" y="36" width="260" height="46" rx="9" /><text x="480.0" y="64" textAnchor="middle" style={{fontSize:"13px"}}>quantity = 3</text></g></g><line className="ngc-link ngc-flow c-slate" x1="480" y1="83" x2="480" y2="99" markerEnd="url(#ngc-g2)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-green"><rect x="350" y="100" width="260" height="46" rx="9" /><text x="480.0" y="120" textAnchor="middle" style={{fontSize:"13px"}}>quantity.update(q =&gt; q + 1)</text><text className="ngc-sub" x="480.0" y="138" textAnchor="middle">uses the old value</text></g></g><line className="ngc-link ngc-flow c-slate" x1="480" y1="147" x2="480" y2="163" markerEnd="url(#ngc-g2)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-green"><rect x="350" y="164" width="260" height="46" rx="9" /><text x="480.0" y="192" textAnchor="middle" style={{fontSize:"13px"}}>quantity = 4</text></g></g>
</svg>
<figcaption>set() replaces the value. update() calculates it from the current value.</figcaption>
</figure>

- **`set(value)`** replaces the value with a new value. Example: `quantity.set(1)`.
- **`update(fn)`** calculates the new value from the current value. The function gets the current value and returns the new one. Example: `quantity.update(q => q + 1)`.

Use `set()` when the new value does not depend on the old one. Use `update()` when it does.

You cannot change a signal with `=`. `this.quantity = 5` is an error, because `quantity` is a signal, not a number. Always use `set()` or `update()`.

```angular runnable title="set() and update()"
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <p>Quantity: {{ quantity() }}</p>
    <button (click)="decrease()">-</button>
    <button (click)="increase()">+</button>
    <button (click)="reset()">Reset</button>
  `,
})
export class App {
  quantity = signal(1);

  increase() {
    this.quantity.update(q => q + 1);
  }

  decrease() {
    this.quantity.update(q => (q > 1 ? q - 1 : 1));
  }

  reset() {
    this.quantity.set(1);
  }
}
```

### Updating Lists and Objects

A signal reports a change only when it gets a **new** value. If you change the old list with `push()`, the signal still holds the same list, so it reports no change. Then a computed signal that reads the list, such as a count, keeps its old value.

Create a new list instead. `[...list, item]` copies the old items into a new list and adds the new item at the end.

```angular runnable title="Updating a list in a signal"
import { Component, computed, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="add('Notebook')">Add notebook</button>
    <button (click)="add('Pen')">Add pen</button>
    <button (click)="clear()">Clear</button>
    <p>Items ({{ count() }}): {{ items().join(', ') }}</p>
  `,
})
export class App {
  items = signal<string[]>([]);
  count = computed(() => this.items().length);

  add(name: string) {
    this.items.update(list => [...list, name]);
  }

  clear() {
    this.items.set([]);
  }
}
```

`count` is a computed signal. You learn computed signals in the next section.

For an object, copy it in the same way: `user.update(u => ({ ...u, name: 'Asha' }))` creates a new object with a new `name`.

## Computed Signals

A **computed signal** calculates its value from other signals. You create it with `computed()` and a function:

```ts
total = computed(() => this.price() * this.quantity());
```

- Angular runs the function again **only when** `price` or `quantity` changes.
- If they do not change, Angular reuses the last result. So reading `total()` many times is fast.
- A computed signal is **read-only**. It has no `set()` or `update()`.

```angular runnable title="A cart total with computed()"
import { Component, computed, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <p>Keyboard: $40 × {{ quantity() }}
      <button (click)="quantity.update(q => q + 1)">+1</button></p>
    <p>Subtotal: {{ subtotal() }}</p>
    <p>Tax (10%): {{ tax() }}</p>
    <p><b>Total: {{ total() }}</b></p>
  `,
})
export class App {
  price = signal(40);
  quantity = signal(1);

  subtotal = computed(() => this.price() * this.quantity());
  tax = computed(() => this.subtotal() * 0.1);
  total = computed(() => this.subtotal() + this.tax());
}
```

Click **+1**. `subtotal` changes because `quantity` changed. `tax` and `total` change because they read `subtotal`.

## Effects

An **effect** is a function that Angular runs again each time a signal that it reads changes. You create it with `effect()`.

<figure className="ngc-fig">
<svg viewBox="0 0 640 134" role="img" aria-label="A signal changes, Angular runs the effect again, and the effect writes a log line or saves a value.">
<defs><marker id="ngc-g3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-blue"><rect x="6" y="30" width="145" height="64" rx="9" /><text x="78.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>theme.set("dark")</text><text className="ngc-sub" x="78.5" y="77" textAnchor="middle">a signal changes</text></g></g><line className="ngc-link ngc-flow c-slate" x1="152" y1="62" x2="166" y2="62" markerEnd="url(#ngc-g3)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-orange"><rect x="167" y="30" width="145" height="64" rx="9" /><text x="239.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Angular</text><text className="ngc-sub" x="239.5" y="77" textAnchor="middle">finds effects that read it</text></g></g><line className="ngc-link ngc-flow c-slate" x1="313" y1="62" x2="327" y2="62" markerEnd="url(#ngc-g3)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-purple"><rect x="328" y="30" width="145" height="64" rx="9" /><text x="400.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>effect(() =&gt; ...)</text><text className="ngc-sub" x="400.5" y="77" textAnchor="middle">runs again</text></g></g><line className="ngc-link ngc-flow c-slate" x1="474" y1="62" x2="488" y2="62" markerEnd="url(#ngc-g3)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="489" y="30" width="145" height="64" rx="9" /><text x="561.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Side effect</text><text className="ngc-sub" x="561.5" y="77" textAnchor="middle">log, save, title</text></g></g><circle className="ngc-packet c-blue" r="5"><animateMotion dur="3.6s" repeatCount="indefinite" path="M78 106 L562 106" /></circle><text className="ngc-note" x="320" y="128" textAnchor="middle">Effects are for work outside the screen. Values for the screen come from computed().</text>
</svg>
<figcaption>An effect runs again each time a signal it reads changes.</figcaption>
</figure>

Use an effect for work **outside** the template, for example:

- Writing a log message.
- Saving a value in the browser's storage.
- Changing the browser tab title.

Create the effect in the constructor. Like `inject()`, `effect()` works only while Angular creates the class.

```angular runnable title="An effect writes a log line"
import { Component, effect, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="theme.set('light')">Light</button>
    <button (click)="theme.set('dark')">Dark</button>
    <p>Current theme: {{ theme() }}</p>
    @for (line of log(); track $index) {
      <p>{{ line }}</p>
    }
  `,
})
export class App {
  theme = signal('light');
  log = signal<string[]>([]);

  constructor() {
    effect(() => {
      const value = this.theme();
      this.log.update(list => [...list, `Theme saved: ${value}`]);
    });
  }
}
```

The effect runs once at the start, and again each time you click a different theme.

Do not use an effect to calculate a value from other signals. Use `computed()` for that. An effect is only for work outside the screen, such as logging or saving.

## Signals vs RxJS

You learned RxJS Observables in the previous chapter. Signals and Observables solve different problems, and Angular apps use both.

<figure className="ngc-fig">
<svg viewBox="0 0 640 140" role="img" aria-label="A signal stores one current value; an Observable sends a series of values over time.">
<defs><marker id="ngc-g4" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><text className="ngc-title c-purple" x="150" y="22" textAnchor="middle">Signal: one current value</text><text className="ngc-title c-orange" x="470" y="22" textAnchor="middle">Observable: values over time</text><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-purple"><rect x="70" y="40" width="160" height="70" rx="9" /><text x="150.0" y="73" textAnchor="middle" style={{fontSize:"16px"}}>count = 3</text><text className="ngc-sub" x="150.0" y="91" textAnchor="middle">read any time: count()</text></g></g><line className="ngc-link c-slate" x1="330" y1="80" x2="620" y2="80" /><g className="ngc-node ngc-soft c-orange ngc-pop ngc-p1"><rect x="350" y="62" width="40" height="36" rx="18" /><text x="370" y="86" textAnchor="middle">1</text></g><g className="ngc-node ngc-soft c-orange ngc-pop ngc-p2"><rect x="440" y="62" width="40" height="36" rx="18" /><text x="460" y="86" textAnchor="middle">2</text></g><g className="ngc-node ngc-soft c-orange ngc-pop ngc-p3"><rect x="530" y="62" width="40" height="36" rx="18" /><text x="550" y="86" textAnchor="middle">3</text></g><text className="ngc-note" x="470" y="124" textAnchor="middle">subscribe() receives each value when it happens</text>
</svg>
<figcaption>Signals hold state. Observables deliver events over time.</figcaption>
</figure>

| Point | Signal | Observable (RxJS) |
|---|---|---|
| Holds | One current value, always | A series of values over time |
| Read the value | `count()`, at any time | `subscribe()` or the `async` pipe |
| Has a value at the start | Yes | Not always |
| Clean up | Not needed | Unsubscribe from Observables that never complete |
| Best for | Data shown on the screen | Events and server requests, with operators such as `switchMap()` |

A simple rule: store **data** in signals, and use RxJS for **events over time**, such as a search box that sends requests.

## Signals Best Practices

- **Call the signal with `()`** to read its value, in both the template and the class.
- **Use `computed()` for values that come from other signals**, for example a total from a price and a quantity. Do not copy the result into another writable signal.
- **Create new lists and objects** in `update()`. Do not change them with `push()` or by setting a property.
- **Use effects only for work outside the screen**, such as logging and saving.
- **Keep writable signals private in services.** Give other classes a read-only version with `asReadonly()`, and change the value only with the service's methods:

```ts
export class CartService {
  private readonly _count = signal(0);
  readonly count = this._count.asReadonly(); // other classes can read, not set

  add() {
    this._count.update(n => n + 1);
  }
}
```

## Common Mistakes

<div className="ngc-mistakes">

| Mistake | What you see | Fix |
|---|---|---|
| `{{ price }}` without `()` | The page shows `[Signal: 15]`, not `15` | Write `{{ price() }}` |
| `items().push('Pen')` | Computed signals that read the list keep their old value | Use `items.update(list => [...list, 'Pen'])` |
| `total.set(10)` on a computed signal | Error: `set` is not a function | Change the signals that `total` reads |
| Calculating a value inside an effect | Extra code, and values can be one step behind | Use `computed()` |
| Calling `effect()` in a click method | Error: effect() must be called from an injection context | Create effects in the constructor |

</div>

## Check Yourself

## Exercises

## What to Remember

- A signal stores one value. Create it with `signal()`, read it with `()`, and change it with `set()` or `update()`. For lists and objects, always create a new one.
- `computed()` calculates a read-only value from other signals and recalculates only when they change.
- `effect()` runs code outside the screen when signals change. Use signals for data on the screen, and RxJS for events over time.

Next: **State Management**, how to store data that many components share.
