Entwicklung von Formularen: Signal-Forms
Angular setzt seit gut zwei Jahren auf die sog. Signals. Signals werden verwendet, um den aktuellen Zustand (State) einer Web-Komponenten oder einer ganzen Web-App auf elegante Weise zu speichern und für die Anzeige zu verwenden.
Ein einfaches Beispiel wäre ein fiktiver Taschenrechner, der zwei Zahlen addieren kann. Die beiden Eingabezahlen würden in Angular als Signals angelegt.
Es gibt einige Arten von speziellen Signals, zum Beispiel computed(), also abgeleitete oder berechnete Signals. In unserem Beispiel kann ein Computed Signal verwendet werden, um das Ergebnis des Taschenrechners zu ermitteln.
Durch die Nutzung von Signals ist der Ergebnis-Wert nun immer garantiert richtig und aktuell. Das Ergebnis reagiert automatisch auf Änderungen von einem oder beiden Eingabewerten.
const a = signal(0);
const b = signal(0);
const result = computed(() => a() + b());
Das result() kann nun direkt im HTML-Code genutzt werden, um das Ergebnis anzuzeigen.
<!-- Eingabefelder hier ... -->
Das Ergebnis ist {{result()}}.
Das ist schon ein riesen Vorteil und erleichtert die Entwicklung enorm. Angular Signals haben seither Einzug gehalten in die gesamte Entwicklungsarbeit und sind der neue Standard.
Ein wichtiger Bestandteil der Anwendungsentwicklung, der noch nicht perfekt mit Signals integriert war, waren Formulare, also Eingabefelder für Zahlen, Text, An/Aus, Mehrfachauswahl, usw.
Mit Angular 21 gibt es nun eine native, auf Signals basierende Implementierung, die eine sehr saubere und einfache Entwicklung von Formularfeldern ermöglicht – mit allen Vorteilen von Signals, wie Reaktivität usw. Es folgt das Taschenrechner-Beispiel mit der Nutzung der neuen Signal-Forms.
import { signal, computed } from '@angular/core';
import { form, Field } from '@angular/forms/signals';
@Component({
imports: [Field],
template: `
A: <input [field]="calculatorForm.a">
B: <input [field]="calculatorForm.b">
Ergebnis: {{result()}}
`
})
export class LoginForm {
calculator = signal({
a: '',
b: ''
});
calculatorForm = form(this.calculator);
result = computed(() => this.calculator().a + this.calculator().b);
}
Das neue Formular funktioniert nun wie folgt:
mit
signal()wird das Datenmodell (hier die Zahlen a und b) angelegtmit
form()wird daraus ein interaktives und reaktives FormularIm Template werden die
input-Elemente mittelsfieldan das entsprechende Formular-Feld gebundenDie Ergebnis-Berechnung bleibt unverändert
Das Ergebnis ist sehr sauberer und einfach zu verstehender Code. Durch Signals ist garantiert, dass alle Eingaben immer synchron sind zum Ergebnis, ohne das für diese Synchronisierung und Reaktivität eigener Code geschrieben werden muss.
Ich freue mich, dieses neue Feature ab jetzt einzusetzen!
Verbesserungen der Barrierefreiheit mit Angular Aria
Mit Angular Aria wurden neue Werkzeuge veröffentlicht, die es uns Entwicklern ermöglichen, barrierefreie Komponenten zu bauen.
Barrierefreiheit ist wichtig, damit alle Menschen das Internet gleichermaßen nutzen können, möglichst unabhängig von ihrem Sehvermögen oder der Fähigkeit, eine Maus zu bedienen.
Um Web-Inhalte barrierefrei zu machen, können wir etwa dafür sorgen, dass auch eine Navigation und Interaktion nur mittels Tastatur möglich ist oder das die Bedeutung von Symbolen und Bildern mit erklärendem Text versehen werden, sodass das Sehen und Erkennen nicht mehr notwendig ist.
Angular Aria bringt nun eine Reihe neuer Werkzeuge, mit denen man Angular-spezifisch die Barrierefreiheit erhöhen kann. Sie bauen auf den allgemeinen Vorgaben und Web-Standards auf und machen es uns Angular-Entwicklern leichter, saubere, zugängliche Anwendungen zu entwickeln.
Neuer Standard-Test-Runner: Vitest
In der Software-Entwicklung schreiben wir Tests, um unseren Code gegenüber den Erwartungen abzusichern und geben uns als Entwicklern die Sicherheit, dass zukünftige Änderungen nicht versehentlich bereits existierende Funktionen beschädigen.
Anhand des Taschenrechner-Beispiels könnten wir etwa einen Test schreiben, der erwartet, dass "1 +1 = 2" ergibt.
import { expect, test } from 'vitest';
import { add } from './calculator';
test('1 + 1 = 2', () => {
expect(add(1, 1)).toBe(2);
});
Wenn wir nun die Taschenrechner-Implementierung – versehentlich – ändern und das Plus durch ein Minus ersetzen, dann würde unser Test fehlschlagen und wir könnten den Fehler bemerken.
Auf diese Weise kann man alle möglichen kleinen und großen Bestandteile einer Software testen und absichern.
Es gibt verschiedene sog. Test-Tunner, die letztlich die geschriebenen Tests ausführen und die Ergebnisse abgleichen. Der inzwischen populärste ist Vitest und Angular macht diesen nun auch zum Standard. Durch die offizielle Integration und Unterstützung von Angular ist die Nutzung von Vitest nun sehr simpel und ohne Mehraufwand oder Workarounds möglich.
Das freut mich sehr, da ich gerade in letzter Zeit wieder mehr Zeit in Tests investiere und diese Neuerung macht das ganze im Angular-Kontext nochmal etwas angenehmer.
Neue Einstellmöglichkeiten für @defer
Seit einigen Versionen gibt es mit dem @defer-Block die Möglichkeit, gewisse Teile einer Web-Anwendung erst später zu laden und somit die Ladenzeit und Netzwerkauslastung zu optimieren.
Das ist zum Beispiel nützlich, wenn ich in meiner Anwendung eine "große" Komponente wie einen Video-Player erst laden und anzeigen möchte, wenn weit genug heruntergescrollt wurde, um den Player sehen zu können.
Dann kann ich mit @defer angeben, dass der entsprechende Block erst beim Eintritt in den Viewport, also ins Sichtfeld des Browsers, geladen werden soll.
@defer (on viewport) {
<video-player/>
}
Mit Angular 21 gibt es nun die Möglichkeit, feiner einzustellen, wann der Inhalt eines @defer-Blocks gezeigt werden soll. Dafür lassen sich nun die Optionen des zugrundeliegenden IntersectionObserver anpassen.
<div #trigger>
Player laden, sobald dieser Abschnitt 100px
vom Bildschirmrand entfernt ist.
</div>
@defer (on viewport({ trigger, rootMargin: '100px' })) {
<video-player/>
}