Angular Signal Inputs einfach erklärt

Nach der Einführung von Signals in Angular wurde ein weiterer Schritt unternommen, um Angular-Apps reaktiver zu machen. Seit Angular 17.1 gibt es ein neues Feature, welche den bisherigen @Input-Dekorator ersetzt und auf Signals aufbaut.

In diesem Artikel werden wir uns mit den neuen Angular Signal Inputs beschäftigen, welche Vorteile sie bieten und wie man sie in seiner Applikation einsetzen kann.

Klassische Methode für Eingabe-Parameter mit @Input-Dekorator

Sehen wir uns ein Beispiel an, wie wir bisher den @Input-Dekorator verwendet haben.
Wir haben hier eine einfache Komponente, welche nichts anderes macht, als einen übergebenen Namen auszugeben:

@Component({
  selector: "app-name",
  standalone: true,
  template: ` <h1>Dein Name lautet: {{ name }}</h1>`,
})
export class NameComponent implements OnChanges {
  
  @Input()
  name = '';

  ngOnChanges(changes: SimpleChanges) {
    const change = changes["value"];

    if (change) {
      console.log(
      `Neuer Wert für den Namen: ${change.currentValue}`);
    }
  }
}Code-Sprache: TypeScript (typescript)

Verwenden würden wir diese Komponente in einer anderen folgendermaßen:

<app-name [name]="'Anton Dvorak'"> // direkter Wert
// oder
<app-name [name]="username">       // Wert aus einem PropertyCode-Sprache: JavaScript (javascript)

Wir sehen hier, dass wir den Namen welcher ausgegeben werden soll, als Attribut übergeben und in der NameComponent wird dieser mit @Input() dekoriert und somit als Eingabe-Parameter gekennzeichnet.

Weiters haben wir dort einen ngOnChanges-Event eingebaut. Das gibt uns die Möglichkeit, auf Änderungen im Wert des Parameters zu reagieren. Also jedesmal, wenn die Komponente, welche unsere NameComponent verwendet, den Wert des Übergabeparameters „name“ ändert, können wir darauf reagieren und Code ausführen (in diesem Fall ein einfaches console.log).

Das ist soweit klassische Angular-Programmierung. Jetzt sehen wir uns an, wie wir denselben Anwendungsfall mit Input Signals umsetzen würden:

Verwendung der neuen Input-Signals

So sieht unsere NameComponent aus, wenn wir Input-Signals verwenden:

import { Component, input } from "@angular/core";

@Component({
  selector: "app-name",
  standalone: true,
  template: ` <h1>Dein Name lautet: {{ name() }}</h1>`,
})
export class NameComponent {

  name = input('');

}Code-Sprache: TypeScript (typescript)

Wie man sieht verwenden wir nun die Funktion input(), welche wir gleich mit einem Default-Wert initialisieren, hier ein leerer String. Ein weiterer kleiner Unterschied ist, dass die Variable name nun automatisch ein Signal ist, wir müssen es jetzt mit () abrufen:

vorher: template: ` <h1>Dein Name lautet: {{ name }}</h1>`,
mit Signals: template: ` <h1>Dein Name lautet: {{ name() }}</h1>`,

Siehe dazu auch unseren Artikel über Angular Signals für weitergehende Informationen.

In unserer Eltern-Komponente, welche die NameComponent verwendet, ändert sich gar nichts. Weiterhin wird der Parameter über den „name“-Property gesetzt:

<app-name [name]="'Anton Dvorak'"> // direkter Wert
// oder
<app-name [name]="username">       // Wert aus einem PropertyCode-Sprache: JavaScript (javascript)

Vorteile von Input Signals

Ein Vorteil der neuen Input Signals ist, wir können unsere Komponenten reaktiver aufbauen, d.h. mit weniger Aufwand auf Änderungen in den Eingabewerten reagieren. Wir haben somit grundsätzlich alle Vorteile, die Signals mit sich bringen, auch für Input-Variablen.

So können wir z.B. computed-Signale, also aus Signalen berechnete neue Signale auch für die Input-Variablen verwenden. Bsp.:

import { Component, computed, input } from '@angular/core';

@Component({
  selector: 'app-name',
  standalone: true,
  template: ` <h1>Dein Name lautet: {{ name() }}</h1>
  <br>{{begruessung()}}
  `,
})
export class NameComponent {
  name = input('');

  begruessung = computed(() => 'Guten Tag, ' + this.name());
}
Code-Sprache: TypeScript (typescript)

„begruessung“ ist ein berechnetes Signal, welches sich automatisch immer ändert, wenn sich die im berechneten Signal verwendeten Signal(e) ändern.

Auf Wert-Änderungen reagieren

Wollen wir auf eine Änderung der Input-Variable reagieren, wie wir es in unserem ersten Beispiel mit dem ngOnChanges-Hook gemacht haben, gibt es in Signals die effect()-Funktion:

import { Component, computed, input } from '@angular/core';

@Component({
  selector: 'app-name',
  standalone: true,
  template: ` <h1>Dein Name lautet: {{ name() }}</h1>
  <br>{{begruessung()}}
  `,
})
export class NameComponent {
  name = input('');

  constructor() {
     effect(() => {
        console.log('Der name hat sich geändert: ' + this.name());
     });
  }
}Code-Sprache: TypeScript (typescript)

Optionen für Input Signals

required

Wir können ein Signal Input als required definieren, um zu verhindern, dass unsere Komponente verwendet wird, ohne dass ein Wert für das Input Signal gesetzt wurde:

name = input.required<number>();Code-Sprache: HTML, XML (xml)

Würden wir nun die Komponente verwenden ohne einen name-Parameter zu setzen, bekommen wir folgenden Fehler:

<app-name></app-name>Code-Sprache: HTML, XML (xml)
✘ [ERROR] NG8008: Required input 'name' from component NameComponent must be specified.Code-Sprache: JavaScript (javascript)

Alias setzen

Für das Input Signal kann ein Alias gesetzt werden, mit welchem er in der übergeordneten Komponente angesprochen werden kann. z.B.

  name = input('', {
    alias: 'benutzername'
  });Code-Sprache: TypeScript (typescript)

Nun muss die Komponente folgendermaßen verwendet werden:

    <app-name [benutzername]="'Anton Dvorak'"></app-name>Code-Sprache: HTML, XML (xml)

Wert transformieren

Wir können den Eingabewert auch automatisch transformieren lassen, in dem wir den transform-Parameter angeben:

  name = input('', {
    transform: (val: string) => val.toUpperCase()
  });Code-Sprache: TypeScript (typescript)

Fazit

Die neuen Angular Signal Inputs geben uns eine elegante Möglichkeit zur Hand auf den @Input-Dekorator verzichten zu können und gleichzeitig die Vorteile der neuen Angular Signals für Input-Parameter von Komponenten nutzen zu können.

Die Beispiel-Komponenten können auf https://stackblitz.com/edit/stackblitz-starters-vpn6l4?file=src%2Fname%2Fname.component.ts getestet werden.