Skip to main content

Signals

A signal is a wrapper around a value that can notify interested consumers when that value changes. Because Angular Signals granularly track how and where your state is used throughout an application, the framework can optimize rendering updates.

As Angular must be able to detect changes of the signal value, it must be immutable.

Signal functions​

signal() - stable since 17.0​

Creates a writeable signal, that can be updated using x.set() or x.update() methods.

const counter = signal(0);

counter.set(2);
counter.update(count => count + 1);

computed() - stable since 17.0​

Memoizing signal, which calculates its value from the values of other signals. A computed signal is not writable. Note that it is not allowed writing to signals in a computed by default. This is a safety facility that no cycles are created.

const counter = signal(0);

// Automatically updates when `counter` changes:
const isEven = computed(() => counter() % 2 === 0);

effect() - developer preview since 17.0​

Get notified when signals have changed their value. The effect() function can only be used in an injection context. Note that it is not allowed writing to signals in a effect by default. This is a safety facility that no cycles are created. If you really want to write signals, set allowSignalWrites in the CreateEffectOptions.

const counter = signal(0);
effect(() => console.log('The counter is:', counter()));
// The counter is: 0

counter.set(1);
// The counter is: 1

input() - developer preview - since 17.1​

Signal input is a signal-based alternative to the traditional @Input decorator

@Component({
template: `<h1>Counter value: {{ value() }}</h1>`,
})
export class Component {
// returns Signal<number>
value = input(0);

// returns Signal<string>
firstName = input.required<string>();

// returns Signal<string | undefined>
lastName = input<string>();
}

Signal queries - developer preview since 17.2​

Signal queries are a signal-based alternative to the traditional @ViewChild, @ViewChildren, @ContentChild or @ContentChildren decorator

import {contentChild, contentChildren, viewChild, viewChildren} from '@angular/core';

@Component({
template: `
<div #el>element to query</div>
<ng-content></ng-content>
`
})
export class Component {
// returns Signal<ElementRef<HTMLDivElement> | undefined>
divEl = viewChild<ElementRef<HTMLDivElement>>('el');

// returns Signal<ElementRef<HTMLDivElement>>
divElRequired = viewChild.required<ElementRef<HTMLDivElement>>('el');

// returns Signal<ReadonlyArray<ElementRef<HTMLDivElement>>>
divEls = viewChildren<ElementRef<HTMLDivElement>>('el');

// returns Signal<TestComponent | undefined>
testComponent = contentChild(TestComponent);

// returns Signal<TestComponent>
testComponentRequired = contentChild.required(TestComponent);

// returns Signal<ReadonlyArray<TestComponent>>
testComponents = contentChildren(TestComponent)
}

model() signals - developer preview since 17.2​

Model signals are exposed as input/output pair to be used by the parent component.

@Component({
selector: 'custom-checkbox',
template: `
<div class="cool-checkbox-treatment">
<input type="checkbox" (click)="toggle()" [value]="checked()">
</div>
`
})
export class CustomCheckbox {
checked = model(false); // returns ModelSignal<number>

toggle() {
this.checked.set(!this.checked());
}
}

output() function - developer preview since 17.3​

output() allows you to define our components output similar to input().

@Component({
selector: 'custom-checkbox',
template: `
<div class="cool-checkbox-treatment">
<input type="checkbox" (click)="checkedChange.emit(!checked())" [value]="checked()">
</div>
`
})
export class CustomCheckbox {
checked = input(false);
checkedChange = output<boolean>(); // returns OutputEmitterRef<boolean>

private nameChange$ = new Subject<string>(); // any observable
nameChange = outputFromObservable(this.nameChange$); // returns OutputEmitterRef<string>

checkedChangeEvents = outputToObservable(this.checkedChange); // returns Observable<boolean>
}

NGRX SignalStore​

EnterJS Angular Day Presentation from Manfred Steyer

Standalone Store from the NGRX team built completely on top of Signals. It is lightweight and independent form the well known NGRX Store based on the Redux pattern. Install using npm i @ngrx/signals.

Debugging Signals​

The Angular Team is planning to add debugging capabilities for signals into the Angular Dev Tools.

Signal Forms​

Because there is currently no support for signals in Forms, the community created ng-signal-forms. This Blog Post is a nice introduction into this library.