![imagen[1]-Tutorial de formas de señal angular (v22) For Windows 7,8,10,11-Winpcsoft.com](https://winpcsoft.com/wp-content/plugins/wp-fastest-cache-premium/pro/images/blank.gif)
Wenn Sie Formulare in Angular erstellt haben, kennen Sie die beiden alten Pfade. Vorlagengesteuerte Formulare sind schnell, aber mit wenigen Typen ausgestattet. Reaktive Formulare sind leistungsstark, bringen aber eine Menge Zeremonien mit sich: FormControl-, FormGroup- und valueChanges-Abonnements, bei denen Sie daran denken müssen, sich abzumelden, und jedes Mal einen ControlValueAccessor, wenn Sie möchten, dass eine benutzerdefinierte Eingabe gut funktioniert.
Formas de señal, stabil in Angular v22, geht einen anderen Weg. Sie definieren Ihren Formularstatus als einfaches Signal, umschließen ihn mit form() und binden Felder in der Vorlage mit [formField]. Gültigkeit, Fehler, berührter Status und Werte werden alle als Signale zurückgegeben, die Sie mit () lesen. Keine Abonnements, kein ControlValueAccessor, vollständige Typinferenz aus Ihrem Modell.
Dieses Tutorial führt das Ganze von Ende zu Ende durch. Wir erstellen ein echtes Checkout-Formular, fügen integrierte und benutzerdefinierte Validatoren hinzu, verknüpfen das Senden mit einer Schaltfläche zum Deaktivieren bei Ungültigkeit und decken dann verschachtelte Gruppen und Array-Felder ab. Jedes Snippet hier wird anhand von @angular/core 22.0.0 und dem Begleitcode-Repo für das Buch überprüft. Ich zeige Ihnen, wie es zusammenpasst.
Wenn Sie zuerst das umfassendere v22-Bild wünschen, lesen Sie Was ist neu in Angular v22?. Dieser Artikel befasst sich mit Formularen.
Live-Demo: Jeder Ausschnitt in diesem Beitrag läuft in der interaktive Signalformen-Demokeine Einrichtung erforderlich.
Was ist die Signal Forms API in Angular v22?
Signalformen leben in @angular/forms/signals (ein separater Einstiegspunkt vom klassischen @angular/forms). Die Kernstücke, die Sie am häufigsten verwenden werden:
Das mentale Modell: Ihr Signal enthält die Daten, form() verpackt sie in einen reaktiven Feldbaum und jedes Feld stellt seine eigenen Signale bereit. Das Modell ist die einzige Quelle der Wahrheit und die Form bleibt in beide Richtungen damit synchronisiert.
Schritt 1: Formstatus als Signal definieren
Beginnen Sie mit einem einfachen beschreibbaren Signal, das die Form Ihres Formulars beschreibt. Die hier eingegebenen Typen fließen durch das gesamte Formular, sodass Sie kostenlos Autovervollständigung und Kompilierungsfehler erhalten.
import { signal } from '@angular/core';
formModel = signal({
method: '',
amount: 100,
});
Das ist die gesamte „Modell“-Ebene. Keine FormGroup, noch keine verschachtelten Builder-Aufrufe, nur ein Objektliteral innerhalb eines Signals.
Schritt 2: Erstellen Sie das Formular mit form()
Wickeln Sie das Modell mit form() ein. Das zweite Argument ist ein Schema-Rückruf, bei dem Sie die Validierung anhand typisierter Pfade im Modell deklarieren.
import { Component, signal, inject } from '@angular/core';
import { form, FormField, required } from '@angular/forms/signals';
@Component({
selector: 'app-checkout-form',
imports: [FormField],
template: `...`,
})
export class CheckoutFormComponent {
formModel = signal({ method: '', amount: 100 });
checkoutForm = form(this.formModel, (schema) => {
required(schema.method, { message: 'Please select a payment method.' });
required(schema.amount, { message: 'Amount is required.' });
});
}
schema.method und schema.amount sind typisierte Pfade, keine Zeichenfolgen. Wenn Sie einen Feldnamen eingeben, schlägt der Build fehl. erforderlich nimmt ein optionales Konfigurationsobjekt mit einer Nachricht, die genau das ist, was Sie dem Benutzer später anzeigen.
checkoutForm ist jetzt ein Feldbaum. Sie können den Status des gesamten Formulars mit checkoutForm() und einzelner Felder mit checkoutForm.method lesen.
Schritt 3: Felder verknüpfen mit [formField]
Binden Sie in der Vorlage jedes Steuerelement mit dem [formField] Direktive, die auf das entsprechende Feld zeigt. Dadurch werden Wertbindungen und Statusaktualisierungen in beide Richtungen durchgeführt, sodass das zugrunde liegende Signal aktuell bleibt, während der Benutzer etwas eingibt oder auswählt.
<form (submit)="onSubmit(); $event.preventDefault()">
<label for="payment-method">Select Payment Method</label>
<select id="payment-method" [formField]="checkoutForm.method">
<option value="">Choose a method...</option>
<option value="credit">Credit Card</option>
<option value="paypal">PayPal</option>
</seleccionar>
<label for="amount">Amount ($)</label>
<input id="amount" type="number" [formField]="checkoutForm.amount" />
<button type="submit" [disabled]="checkoutForm().invalid()">
Pay {{ checkoutForm.amount().value() | currency }}
</button>
</form>
Ein paar Dinge, die es zu beachten gilt:
- [formField]="checkoutForm.method" bindet ein natives
- checkoutForm.amount().value() liest den aktuellen Feldwert als Signal, sodass die Schaltflächenbeschriftung live aktualisiert wird.
- checkoutForm().invalid() liest die Gültigkeit des gesamten Formulars. Wir verwenden es, um das Senden zu deaktivieren.
Denken Sie daran, FormField zum Import-Array der Komponente hinzuzufügen, da eigenständige Komponenten nur Anweisungen sehen, die sie importieren.
Schritt 4: Validierungsfehler mit @if anzeigen
Jedes Feld stellt .invalid(), .touched() und .errors() als Signale bereit. Das übliche Muster besteht darin, Fehler erst anzuzeigen, nachdem der Benutzer das Feld berührt hat, und dann die Fehlerliste zu durchlaufen. Fehler enthalten eine Art und eine optionale Nachricht.
<select id="payment-method" [formField]="checkoutForm.method">
<option value="">Choose a method...</option>
<option value="credit">Credit Card</option>
<option value="paypal">PayPal</option>
</seleccionar>
@if (checkoutForm.method().invalid() && checkoutForm.method().touched()) {
<div class="error-msg">
@for (error of checkoutForm.method().errors(); track error.kind) {
<span>{{ error.message }}</span>
}
</div>
}
track error.kind ist hier der richtige Schlüssel, da jeder Validator eine Fehlerart beisteuert (erforderlich, E-Mail, Ihre benutzerdefinierte Art usw.). Keine Abonnements, keine über Ihre Komponente verstreuten markAsTouched-Aufrufe. Der berührte Zustand wird für Sie getrackt und als Signal ausgegeben.
Schritt 5: Fügen Sie einen benutzerdefinierten Validator hinzu
Integrierte Validatoren decken die häufigsten Fälle ab, echte Formulare erfordern jedoch immer benutzerdefinierte Regeln. Verwenden Sie „validate(path, fn)“. Die Funktion empfängt einen Kontext mit dem Wertsignal des Felds und einen valueOf-Helfer zum Lesen anderer Felder. Geben Sie ein Fehlerobjekt mit einer Art und einer Nachricht zurück oder geben Sie null zurück, wenn das Feld gültig ist.
import { form, required, validate } from '@angular/forms/signals';
checkoutForm = form(this.formModel, (schema) => {
required(schema.method, { message: 'Please select a payment method.' });
// Custom rule: amount must be a positive number under the limit.
validate(schema.amount, ({ value }) => {
const amount = value();
si (amount <= 0) {
return { kind: 'minAmount', message: 'Amount must be greater than zero.' };
}
si (amount > 10000) {
return { kind: 'maxAmount', message: 'Amount cannot exceed $10,000.' };
}
return null;
});
});
Es gibt keinen customError()-Helfer. Ein benutzerdefinierter Fehler ist nur ein einfaches Objekt mit einer Art und einer optionalen Nachricht. Dieses Objekt fließt in das .errors()-Array des Felds ein, sodass es mit derselben @for-Vorlage (error of …; track error.kind) ohne zusätzlichen Aufwand gerendert wird.
Feldübergreifende Validierung
Da der Validatorkontext den Wert „valueOf“ liefert, sind feldübergreifende Regeln unkompliziert. Das Lesen eines anderen Felds mit valueOf ist reaktiv, sodass die Regel erneut ausgeführt wird, wenn sich dieses Feld ändert. Ein Klassiker "Passwörter müssen übereinstimmen" überprüfen:
import { form, required, validate } from '@angular/forms/signals';
signupForm = form(this.signupModel, (schema) => {
required(schema.password);
required(schema.confirmPassword);
validate(schema.confirmPassword, ({ value, valueOf }) => {
si (value() !== valueOf(schema.password)) {
return { kind: 'passwordMismatch', message: 'Passwords do not match.' };
}
return null;
});
});
Schritt 6: Behandeln Sie das Absenden und Deaktivieren, solange es ungültig ist
Sie haben die Schaltfläche „Senden“ bereits mit deaktiviert [disabled]="checkoutForm().invalid()". Achten Sie beim Submit-Handler selbst auf die Gültigkeit und lesen Sie den eingegebenen Wert aus dem Formular. Der Wert wird vollständig von Ihrem Originalmodell eingegeben.
import { Component, signal, inject } from '@angular/core';
import { form, FormField, required } from '@angular/forms/signals';
import { PaymentService } from './payment.service';
export class CheckoutFormComponent {
private paymentService = inject(PaymentService);
paymentResult = signal<{ success: boolean; transactionId: string } | null>(null);
formModel = signal({ method: '', amount: 100 });
checkoutForm = form(this.formModel, (schema) => {
required(schema.method);
required(schema.amount);
});
onSubmit() {
si (this.checkoutForm().valid()) {
const { method, amount } = this.checkoutForm().value();
const result = this.paymentService.processPayment(method, amount);
this.paymentResult.set(result);
}
}
}
Für fortgeschrittenere Abläufe liefert Signal Forms auch eine Submit()-Funktion von @angular/forms/signals. Es führt eine asynchrone Aktion aus, markiert berührte Felder und verweigert die Ausführung, solange das Formular ungültig ist oder eine Übermittlung bereits läuft. Das obige Handbuch zu onSubmit reicht für einfache Fälle aus; Greifen Sie auf „submitte()“ zu, wenn Sie die integrierte Lebenszyklusbehandlung für Serveraufrufe wünschen.
Dieses vollständige Checkout-Beispiel, einschließlich eines barrierefreien Akkordeons, das mit Angular Aria erstellt wurde, befindet sich im Code-Repository des Buchs unter apps/chapter-06/src/app/checkout-form.
Verschachtelte und gruppierte Felder
Verschachtelte Objekte benötigen keine spezielle API. Verschachteln Sie sie im Modell und der Feldbaum spiegelt die Form wider. Sie binden verschachtelte Felder mit gepunkteten Pfaden und validieren sie auf verschachtelten Schemapfaden.
formModel = signal({
customer: {
nombre: '',
correo electrónico: '',
},
amount: 100,
});
checkoutForm = form(this.formModel, (schema) => {
required(schema.customer.name, { message: 'Name is required.' });
required(schema.customer.email, { message: 'Email is required.' });
correo electrónico(schema.customer.email, { message: 'Enter a valid email.' });
});
<input [formField]="checkoutForm.customer.name" />
<input [formField]="checkoutForm.customer.email" />
@if (checkoutForm.customer.email().invalid() && checkoutForm.customer.email().touched()) {
<div class="error-msg">
@for (error of checkoutForm.customer.email().errors(); track error.kind) {
<span>{{ error.message }}</span>
}
</div>
}
email ist neben min, max, minLength, maxLength und Pattern einer der integrierten Validatoren, die alle aus @angular/forms/signals importiert wurden. Jeder akzeptiert die gleiche { message }-Konfiguration, sodass Sie die Kopie steuern können.
Array-Felder
Halten Sie für sich wiederholende Zeilen ein Array in Ihrem Modell und validieren Sie jedes Element mit applyEach. Es wendet ein Schema auf jedes Element im Array an und innerhalb dieses Elementschemas validieren Sie es anhand der eigenen Pfade des Elements.
import { form, required, applyEach } from '@angular/forms/signals';
orderModel = signal({
items: [
{ nombre: '', quantity: 1 },
],
});
orderForm = form(this.orderModel, (schema) => {
applyEach(schema.items, (item) => {
required(item.name, { message: 'Item name is required.' });
required(item.quantity);
});
});
Iterieren Sie in der Vorlage die Elemente des Feldbaums mit @for und binden Sie jede Zeile. Um Zeilen hinzuzufügen oder zu entfernen, aktualisieren Sie das Quellsignal mit .update(); Der Feldbaum reagiert automatisch.
@for (item of orderForm.items; track $index) {
<div class="row">
<input [formField]="item.name" />
<input type="number" [formField]="item.quantity" />
</div>
}
<button type="button" (click)="addItem()">Add item</button>
addItem() {
this.orderModel.update((modelo) => ({
...modelo,
items: [...model.items, { nombre: '', quantity: 1 }],
}));
}
Da es sich bei dem Array nur um Daten in einem Signal handelt, mutieren Sie es mit den unveränderlichen Aktualisierungsmustern, die Sie bereits für Signale verwenden. Es gibt keine FormArray-API, die separat erlernt werden kann.
Signalformen vs. reaktive Formen
Beide sind in Version 22 erstklassig, daher ist dies eine Wahl und kein Migrationsauftrag. Hier ist der ehrliche Vergleich.
Was Signal Forms entfernt, ist an zwei Stellen am auffälligsten: beim ControlValueAccessor-Tanz für benutzerdefinierte Steuerelemente und bei der manuellen Abonnementverwaltung rund um valueChanges. Wenn Ihre Reaktivität bereits signalorientiert ist, sorgen Signalformen dafür, dass Sie durchgängig in einem mentalen Modell bleiben.
Wann Sie Reactive Forms noch verwenden sollten
Seien Sie pragmatisch. Greifen Sie zu reaktiven Formularen, wenn:
- Sie haben bereits eine große Codebasis für Reactive Forms und ein Umschreiben lohnt sich nicht.
- Sie stützen sich stark auf RxJS-Operatoren statt auf valueChanges (entprellte Serversuche, komplexe Stream-Zusammensetzung). Dieses Interop ist heute in der klassischen API ausgereifter.
- Eine Bibliothek eines Drittanbieters, auf die Sie angewiesen sind, erwartet ControlValueAccessor- oder FormGroup-Instanzen.
Für neue Formulare in neuem Code sind Signalformulare in Version 22 die sauberere Standardeinstellung. Für alles andere gilt: Migrieren Sie, wenn der Wert klar ist, und nicht, weil die API neu ist. Wenn Ihr umfassenderes Ziel darin besteht, eine reaktive Codebasis in Richtung Signale zu verschieben, ist die Leitfaden zur Migration von RxJS zu Signalen deckt die Muster ab, und die NgRx-zu-Signale-Anleitung deckt die staatliche Verwaltung ab.
Häufige Fallstricke
Ein paar Dinge, die Menschen früh aus der Fassung bringen:
- Falscher Importpfad. Signalformen stammen von @angular/forms/signals, nicht von @angular/forms. Sie zu verwechseln ist der häufigste erste Fehler.
- Den Import vergessen FormField. Eigenständige Komponenten müssen es in Importen auflisten, oder [formField] tut stillschweigend nichts.
- Fehler vor der Berührung anzeigen. Gate-Fehleranzeige bei .touched(), damit das Formular Benutzer beim ersten Malen nicht anschreit. Kombinieren Sie .invalid() mit .touched().
- Mutieren des Modells an Ort und Stelle. Aktualisieren Sie das Quellsignal unveränderlich mit .set() oder .update(). Eine In-Place-Mutation löst nicht die erwartete Reaktivität aus.
- Erwarte ein customError()-Helfer. Es gibt keine. Benutzerdefinierte Validatoren geben ein einfaches { kind, message }-Objekt oder null zurück.
- Nach greifen FormArray. Arrays sind nur Arrays in Ihrem Signal; Verwenden Sie applyEach für die Validierung pro Element.
Häufig gestellte Fragen
Ist Signal Forms in Angular v22 stabil?
Ja. Signal Forms wurde in Angular v22 auf stabil umgestellt. Sie wurden in Version 21 als Vorschau eingeführt und in Version 22 zusammen mit Angular Aria stabilisiert, mit vollständiger Dokumentation und zusätzlicher Unterstützung für Angular Material und Angular Aria.
Was ist der Importpfad für Signalformen?
Alles kommt von @angular/forms/signals: form, FormField, erforderlich, validieren, E-Mail, min, max, minLength, maxLength, Pattern, applyEach und Submit. Dies ist ein vom klassischen @angular/forms getrennter Einstiegspunkt.
Wie füge ich einen benutzerdefinierten Validator in Signal Forms hinzu?
Verwenden Sie „validate(path, fn)“ innerhalb des Schema-Callbacks. Die Funktion gibt ein einfaches Fehlerobjekt mit einer Art und einer optionalen Nachricht zurück oder null, wenn gültig. Lesen Sie den eigenen Wert des Feldes mit dem value()-Signal des Kontexts und andere Felder mit valueOf(…).
Wie lese ich Formularwerte und Gültigkeit?
Es sind Signale. Lesen Sie einen Feldwert mit myForm.fieldName().value(), die Feldgültigkeit mit .valid() / .invalid(), den berührten Zustand mit .touched() und Fehler mit .errors(). Die Gültigkeit des gesamten Formulars ist myForm().valid() / myForm().invalid().
Ersetzen Signalformen reaktive Formen?
Nein. Beide werden in v22 unterstützt. Signalformen sind die sauberere Standardeinstellung für neuen Signal-First-Code. Reaktive Formulare bleiben eine gute Wahl für bestehende Codebasen und umfangreiche RxJS-over-valueChanges-Workflows.
Wie gehe ich mit Array-Feldern (sich wiederholenden Feldern) um?
Halten Sie ein Array in Ihrem Modellsignal und wenden Sie ein Schema pro Element mit applyEach(schema.items, (item) => { … }) an. Rendern Sie Zeilen mit @for und binden Sie sie jeweils mit [formField]und fügen Sie Zeilen hinzu oder entfernen Sie sie, indem Sie das Quellsignal unveränderlich aktualisieren.
Gehen Sie tiefer
Dieses Tutorial ist der Arbeitskern von Signal Forms, aber ein Produktionsformular bietet mehr: eine zugängliche Struktur mit Angular Aria, eine durch Ressourcen unterstützte asynchrone Validierung und wie Formulare in eine vollständig signalgesteuerte Komponente passen. Kapitel 6 von Winkelsignale beherrschen erstellt das vollständige Checkout-Formular Ende-zu-Ende, indem es Signal Forms mit Angular Aria Headless-Anweisungen für Tastatur- und Screenreader-Unterstützung kombiniert und es mit signalbasierten Diensten mit dem neuen @Service()-Dekorator verbindet. Jedes Beispiel entspricht dem v22-Code-Repo.
Das Buch wurde von einem Google-Entwicklerexperten für KI und Angular geschrieben, mit einem Vorwort vom Angular-Team. Fangen Sie an Leanpubwo Sie PDF und EPUB sowie kostenlose Updates erhalten: https://leanpub.com/mastering-angular-signals. Es ist auch an Amazonas. Ausführliche Informationen zum Buch-Landingpage.
![]()
Das Angular Signal Forms Tutorial (v22) wurde ursprünglich in Google Developer Experts auf Medium veröffentlicht, donde la gente continúa la conversación, destacando y respondiendo a esta historia.
