Skip to content

Angular Signals ​

To update values in a component angular use Zonejs to detect changes in the component and update the view. But zonejs is not always the best solution, for example when you have a lot of data to update in a component, it can slow down the application.

This is why since angular 16 we have the option to use signals to update the view.

Signal ​

With signal we can now explicitly tell angular to update the view when we want to.

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

@Component({
  selector: 'app-signals',
    templateUrl: './signals.component.html',
    styleUrls: ['./signals.component.css']
})
export class SignalsComponent {
    counter = signal<number>(0);

    increment() {
        this.counter.update(value => value + 1);
    }

    decrement() {
        this.counter.update(value => value - 1);
    }
}

And so the view will be updated only when we call the update method.

html
<p>Counter: {{ counter() }}</p>
<button (click)="increment()">Increment</button>
<button (click)="decrement()">Decrement</button>

Set/Update/Mutate ​

We can also use the set, update and mutate methods to update the value of the signal.

  • Set: Set the value of the signal
  • Update: Update the value of the signal .update(oldValue => oldValue + 1)
  • Mutate: Mutate has been removed since angular 17 To update value on mutable objects like arrays or objects
ts
import { signal } from '@angular/core';

@Component({
  selector: 'app-signals',
    templateUrl: './signals.component.html',
    styleUrls: ['./signals.component.css']
})

export class SignalsComponent {
    counter = signal<number>(0);
    itemas = signal<string[]>([]);

    increment() {
        // this.counter.set(counter() + 1); <= you can also use counter() to get the value
        // You can do it anywhere in the component no only in the template
        this.counter.update(value => value + 1);

        // for example
        this.items.mutate(items => [...items, 'item']).push();
    }

    decrement() {
        this.counter.update(value => value - 1);
    }

    reset() {
        this.counter.set(0);
    }
}

Computed ​

We can also use computed signals to compute values from other signals.

ts

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

@Component({
  selector: 'app-signals',
    templateUrl: './signals.component.html',
    styleUrls: ['./signals.component.css']
})
export class SignalsComponent {
    counter = signal<number>(0);
    double = computed(() => this.counter() * 2);

    increment() {
        this.counter.update(value => value + 1);
    }

    decrement() {
        this.counter.update(value => value - 1);
    }
}
html
<p>Counter: {{ counter() }}</p>
<p>Double: {{ double() }}</p>
<button (click)="increment()">Increment</button>
<button (click)="decrement()">Decrement</button>

Angular will memoize the computed value and only update the view when the value changes.

Effect ​

We can also use effects to run side effects when a signal changes. This is used to run code when signals change.

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

@Component({
  selector: 'app-signals',
    templateUrl: './signals.component.html',
    styleUrls: ['./signals.component.css']
})
export class SignalsComponent {
    counter = signal<number>(0);
    double = computed(() => this.counter() * 2);

    increment() {
        this.counter.update(value => value + 1);
    }

    decrement() {
        this.counter.update(value => value - 1);
    }

    constructor() {
        effect(() => {
            console.log('Counter changed:', this.counter());
        });
    }
}

Input Signals ​

We can also use input signals to pass signals to child components.

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

@Component({
  selector: 'app-signals',
    templateUrl: './signals.component.html',
    styleUrls: ['./signals.component.css']
})
export class SignalsComponent {
    name = input<number>(0); // <= input signal
    lastName = input.required<string>(''); // <= required input signal
    age = input(0, { alias: 'userAge' }); // <= input signal with alias, default name is the variable name
    // This allows users to bind to your input using [userAge], while inside your component you can access the input values using this.age.
}

Last words ​

Since angular 18 we should now use input signals instead of @Input to pass data to child components. As mentionned Here

References ​