Angular bietet leistungsstarke und flexible Mechanismen zur Formularverarbeitung, die es Entwicklern ermöglichen, von einfachen Kontaktformularen bis hin zu komplexen mehrstufigen Workflows alles zu implementieren. Dieser Leitfaden behandelt die zwei Hauptansätze für Formulare in Angular: Template-driven Forms und Reactive Forms.
Bevor wir in die spezifischen Implementierungen eintauchen, ist es wichtig, einige grundlegende Konzepte zu verstehen:
Template-driven Forms setzen auf deklarative Syntax im HTML-Template und sind ideal für einfache bis mittlere Formulare. Sie sind näher an traditionellem HTML und daher oft einfacher für Einsteiger.
Um Template-driven Forms zu verwenden, müssen Sie das
FormsModule importieren:
// app.module.ts oder feature.module.ts
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [
FormsModule
],
// ...
})
export class AppModule { }// user-form.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-user-form',
templateUrl: './user-form.component.html',
})
export class UserFormComponent {
user = {
name: '',
email: '',
age: null
};
onSubmit() {
console.log('Formular eingereicht:', this.user);
// Hier würde die Logik zum Senden der Daten an den Server folgen
}
}<!-- user-form.component.html -->
<form #userForm="ngForm" (ngSubmit)="onSubmit()" class="form">
<h2>Benutzerprofil</h2>
<div class="form-group">
<label for="name">Name</label>
<input
type="text"
id="name"
name="name"
[(ngModel)]="user.name"
required
minlength="3"
#nameField="ngModel"
class="form-control">
<div *ngIf="nameField.invalid && (nameField.dirty || nameField.touched)" class="error-messages">
<div *ngIf="nameField.errors?.['required']">
Name ist erforderlich.
</div>
<div *ngIf="nameField.errors?.['minlength']">
Name muss mindestens 3 Zeichen lang sein.
</div>
</div>
</div>
<div class="form-group">
<label for="email">E-Mail</label>
<input
type="email"
id="email"
name="email"
[(ngModel)]="user.email"
required
email
#emailField="ngModel"
class="form-control">
<div *ngIf="emailField.invalid && (emailField.dirty || emailField.touched)" class="error-messages">
<div *ngIf="emailField.errors?.['required']">
E-Mail ist erforderlich.
</div>
<div *ngIf="emailField.errors?.['email']">
Bitte geben Sie eine gültige E-Mail-Adresse ein.
</div>
</div>
</div>
<div class="form-group">
<label for="age">Alter</label>
<input
type="number"
id="age"
name="age"
[(ngModel)]="user.age"
required
min="18"
max="120"
#ageField="ngModel"
class="form-control">
<div *ngIf="ageField.invalid && (ageField.dirty || ageField.touched)" class="error-messages">
<div *ngIf="ageField.errors?.['required']">
Alter ist erforderlich.
</div>
<div *ngIf="ageField.errors?.['min']">
Alter muss mindestens 18 sein.
</div>
<div *ngIf="ageField.errors?.['max']">
Alter kann maximal 120 sein.
</div>
</div>
</div>
<button type="submit" [disabled]="userForm.invalid" class="submit-button">
Speichern
</button>
</form>Die ngForm-Direktive wird automatisch auf jedes
<form>-Element in Ihrer Anwendung angewendet, wenn
das FormsModule importiert ist. Sie können darauf über eine
Template-Referenzvariable zugreifen (wie
#userForm="ngForm").
Die ngModel-Direktive verbindet ein Formularfeld mit
einer Eigenschaft im Komponenten-Code:
[(ngModel)]="user.name" für zweiweg-Datenbindung[ngModel]="user.name" für einweg-Datenbindung (von
Komponente zum Template)(ngModelChange)="handleChange($event)" für
EreignisbehandlungAngular fügt automatisch CSS-Klassen zu Formularfeldern hinzu, basierend auf ihrem Status:
.ng-valid / .ng-invalid:
Validierungsstatus.ng-pristine / .ng-dirty: Ob der Wert
geändert wurde.ng-untouched / .ng-touched: Ob das
Element den Fokus hatte und verloren hatDiese Klassen können für benutzerdefiniertes Styling verwendet werden:
.ng-invalid.ng-touched {
border-color: red;
}
.ng-valid.ng-touched {
border-color: green;
}Template-driven Forms können auch verschachtelte Objekte darstellen:
// nested-form.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-nested-form',
templateUrl: './nested-form.component.html',
})
export class NestedFormComponent {
customer = {
name: '',
address: {
street: '',
city: '',
zipCode: ''
}
};
onSubmit() {
console.log('Formular eingereicht:', this.customer);
}
}<!-- nested-form.component.html -->
<form #customerForm="ngForm" (ngSubmit)="onSubmit()">
<div ngModelGroup="customer">
<input name="name" [(ngModel)]="customer.name" required>
<div ngModelGroup="address">
<input name="street" [(ngModel)]="customer.address.street" required>
<input name="city" [(ngModel)]="customer.address.city" required>
<input name="zipCode" [(ngModel)]="customer.address.zipCode" required>
</div>
</div>
<button type="submit" [disabled]="customerForm.invalid">Speichern</button>
</form>Reactive Forms bieten einen programmatischen Ansatz zur Formularerstellung und -verwaltung. Sie sind besonders nützlich für komplexe Formulare, dynamische Felder und fortgeschrittene Validierungsszenarien.
Um Reactive Forms zu verwenden, müssen Sie das
ReactiveFormsModule importieren:
// app.module.ts oder feature.module.ts
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
ReactiveFormsModule
],
// ...
})
export class AppModule { }// reactive-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-reactive-form',
templateUrl: './reactive-form.component.html',
})
export class ReactiveFormComponent implements OnInit {
userForm!: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.userForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
age: [null, [
Validators.required,
Validators.min(18),
Validators.max(120)
]]
});
}
onSubmit() {
if (this.userForm.valid) {
console.log('Formular eingereicht:', this.userForm.value);
// Hier würde die Logik zum Senden der Daten an den Server folgen
} else {
// Markiere alle Felder als berührt, um Validierungsfehler anzuzeigen
this.markFormGroupTouched(this.userForm);
}
}
// Hilfsfunktion, um alle Felder als berührt zu markieren
markFormGroupTouched(formGroup: FormGroup) {
Object.values(formGroup.controls).forEach(control => {
control.markAsTouched();
if ((control as FormGroup).controls) {
this.markFormGroupTouched(control as FormGroup);
}
});
}
// Einfacher Zugriff auf Formularsteuerelemente
get nameControl() { return this.userForm.get('name'); }
get emailControl() { return this.userForm.get('email'); }
get ageControl() { return this.userForm.get('age'); }
}<!-- reactive-form.component.html -->
<form [formGroup]="userForm" (ngSubmit)="onSubmit()" class="form">
<h2>Benutzerprofil (Reactive)</h2>
<div class="form-group">
<label for="name">Name</label>
<input
type="text"
id="name"
formControlName="name"
class="form-control">
<div *ngIf="nameControl?.invalid && (nameControl?.dirty || nameControl?.touched)" class="error-messages">
<div *ngIf="nameControl?.errors?.['required']">
Name ist erforderlich.
</div>
<div *ngIf="nameControl?.errors?.['minlength']">
Name muss mindestens 3 Zeichen lang sein.
</div>
</div>
</div>
<div class="form-group">
<label for="email">E-Mail</label>
<input
type="email"
id="email"
formControlName="email"
class="form-control">
<div *ngIf="emailControl?.invalid && (emailControl?.dirty || emailControl?.touched)" class="error-messages">
<div *ngIf="emailControl?.errors?.['required']">
E-Mail ist erforderlich.
</div>
<div *ngIf="emailControl?.errors?.['email']">
Bitte geben Sie eine gültige E-Mail-Adresse ein.
</div>
</div>
</div>
<div class="form-group">
<label for="age">Alter</label>
<input
type="number"
id="age"
formControlName="age"
class="form-control">
<div *ngIf="ageControl?.invalid && (ageControl?.dirty || ageControl?.touched)" class="error-messages">
<div *ngIf="ageControl?.errors?.['required']">
Alter ist erforderlich.
</div>
<div *ngIf="ageControl?.errors?.['min']">
Alter muss mindestens 18 sein.
</div>
<div *ngIf="ageControl?.errors?.['max']">
Alter kann maximal 120 sein.
</div>
</div>
</div>
<button type="submit" [disabled]="userForm.invalid" class="submit-button">
Speichern
</button>
</form>Der FormBuilder-Service bietet syntaktischen Zucker für die Erstellung von Formularen:
// Mit FormBuilder
this.form = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]]
});
// Ohne FormBuilder (äquivalent)
this.form = new FormGroup({
name: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email])
});Reactive Forms bieten Observables für Änderungen an Werten und Status:
// Auf Wertänderungen reagieren
this.form.valueChanges.subscribe(value => {
console.log('Formularwerte geändert:', value);
});
// Auf Statusänderungen reagieren
this.form.statusChanges.subscribe(status => {
console.log('Formularstatus geändert:', status);
});Es gibt mehrere Methoden, um Werte in einem Reactive Form zu aktualisieren:
// Alle Werte ersetzen (benötigt Werte für alle Felder)
this.form.setValue({
name: 'Max Mustermann',
email: 'max@example.com',
age: 30
});
// Teilweise Werte aktualisieren
this.form.patchValue({
name: 'Max Mustermann'
});
// Einzelnes Feld aktualisieren
this.form.get('name')?.setValue('Max Mustermann');
// Formular zurücksetzen (auf Initialwerte)
this.form.reset();Reactive Forms eignen sich besonders gut für komplexe, verschachtelte Strukturen:
// nested-reactive-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-nested-reactive-form',
templateUrl: './nested-reactive-form.component.html',
})
export class NestedReactiveFormComponent implements OnInit {
customerForm!: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.customerForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
address: this.fb.group({
street: ['', Validators.required],
city: ['', Validators.required],
zipCode: ['', [
Validators.required,
Validators.pattern(/^\d{5}$/)
]]
})
});
}
onSubmit() {
if (this.customerForm.valid) {
console.log('Formular eingereicht:', this.customerForm.value);
}
}
// Getter für verschachtelte Formularkontrollen
get name() { return this.customerForm.get('name'); }
get street() { return this.customerForm.get('address.street'); }
get city() { return this.customerForm.get('address.city'); }
get zipCode() { return this.customerForm.get('address.zipCode'); }
}<!-- nested-reactive-form.component.html -->
<form [formGroup]="customerForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">Name</label>
<input id="name" formControlName="name">
<div *ngIf="name?.invalid && name?.touched" class="error">
Name ist erforderlich und muss mindestens 3 Zeichen lang sein.
</div>
</div>
<div formGroupName="address">
<h3>Adresse</h3>
<div class="form-group">
<label for="street">Straße</label>
<input id="street" formControlName="street">
<div *ngIf="street?.invalid && street?.touched" class="error">
Straße ist erforderlich.
</div>
</div>
<div class="form-group">
<label for="city">Stadt</label>
<input id="city" formControlName="city">
<div *ngIf="city?.invalid && city?.touched" class="error">
Stadt ist erforderlich.
</div>
</div>
<div class="form-group">
<label for="zipCode">PLZ</label>
<input id="zipCode" formControlName="zipCode">
<div *ngIf="zipCode?.invalid && zipCode?.touched" class="error">
<span *ngIf="zipCode?.errors?.['required']">PLZ ist erforderlich.</span>
<span *ngIf="zipCode?.errors?.['pattern']">PLZ muss aus 5 Ziffern bestehen.</span>
</div>
</div>
</div>
<button type="submit" [disabled]="customerForm.invalid">Speichern</button>
</form>Ein besonders mächtiges Feature von Reactive Forms ist die Möglichkeit, dynamische Formulare mit FormArray zu erstellen:
// dynamic-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html',
})
export class DynamicFormComponent implements OnInit {
orderForm!: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.orderForm = this.fb.group({
customerName: ['', Validators.required],
items: this.fb.array([
this.createItem()
])
});
}
// Getter für die FormArray
get items() {
return this.orderForm.get('items') as FormArray;
}
// Methode zum Erstellen eines neuen Artikel-FormGroups
createItem(): FormGroup {
return this.fb.group({
productName: ['', Validators.required],
quantity: [1, [Validators.required, Validators.min(1)]],
price: [0, [Validators.required, Validators.min(0.01)]]
});
}
// Methode zum Hinzufügen eines neuen Artikels
addItem() {
this.items.push(this.createItem());
}
// Methode zum Entfernen eines Artikels
removeItem(index: number) {
this.items.removeAt(index);
}
// Berechnung der Gesamtsumme
get totalPrice() {
return this.items.controls
.map(control => control.get('quantity')?.value * control.get('price')?.value)
.reduce((acc, val) => acc + val, 0);
}
onSubmit() {
if (this.orderForm.valid) {
console.log('Bestellung eingereicht:', this.orderForm.value);
console.log('Gesamtpreis:', this.totalPrice);
}
}
}<!-- dynamic-form.component.html -->
<form [formGroup]="orderForm" (ngSubmit)="onSubmit()">
<h2>Bestellformular</h2>
<div class="form-group">
<label for="customerName">Kundenname</label>
<input id="customerName" formControlName="customerName">
<div *ngIf="orderForm.get('customerName')?.invalid && orderForm.get('customerName')?.touched" class="error">
Kundenname ist erforderlich.
</div>
</div>
<h3>Artikel</h3>
<div formArrayName="items">
<div *ngFor="let item of items.controls; let i = index" [formGroupName]="i" class="item-row">
<div class="form-group">
<label [for]="'productName' + i">Produktname</label>
<input [id]="'productName' + i" formControlName="productName">
</div>
<div class="form-group">
<label [for]="'quantity' + i">Menge</label>
<input [id]="'quantity' + i" type="number" formControlName="quantity">
</div>
<div class="form-group">
<label [for]="'price' + i">Preis</label>
<input [id]="'price' + i" type="number" step="0.01" formControlName="price">
</div>
<button type="button" class="remove-button" (click)="removeItem(i)" *ngIf="items.length > 1">
Entfernen
</button>
</div>
<button type="button" (click)="addItem()" class="add-button">
Artikel hinzufügen
</button>
</div>
<div class="total">
<strong>Gesamt: {{ totalPrice | currency:'EUR' }}</strong>
</div>
<button type="submit" [disabled]="orderForm.invalid" class="submit-button">
Bestellen
</button>
</form>Angular bietet verschiedene Validatoren in
@angular/forms. Die häufigsten sind:
Validators.required: Feld darf nicht leer seinValidators.minLength(n): Mindestlänge von n
ZeichenValidators.maxLength(n): Höchstlänge von n ZeichenValidators.pattern(regex): Überprüfung gegen einen
regulären AusdruckValidators.email: Validierung einer E-Mail-AdresseValidators.min(n): Mindestwert (für numerische
Felder)Validators.max(n): Höchstwert (für numerische
Felder)Für spezielle Validierungsanforderungen können Sie benutzerdefinierte Validatoren erstellen:
// custom-validators.ts
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
// Validator-Funktion für übereinstimmende Passwörter
export function matchingPasswordsValidator(passwordKey: string, confirmPasswordKey: string): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const password = control.get(passwordKey);
const confirmPassword = control.get(confirmPasswordKey);
if (!password || !confirmPassword) {
return null;
}
return password.value === confirmPassword.value ? null : { passwordsMismatch: true };
};
}
// Validator-Funktion für eine deutsche Telefonnummer
export function germanPhoneValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!control.value) {
return null;
}
// Einfache Regex für deutsche Telefonnummern
const isValid = /^(\+49|0)[0-9]{7,14}$/.test(control.value);
return isValid ? null : { invalidGermanPhone: true };
};
}Und ihre Anwendung:
// registration-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { matchingPasswordsValidator, germanPhoneValidator } from './custom-validators';
@Component({
selector: 'app-registration-form',
templateUrl: './registration-form.component.html',
})
export class RegistrationFormComponent implements OnInit {
registrationForm!: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.registrationForm = this.fb.group({
username: ['', [Validators.required, Validators.minLength(4)]],
email: ['', [Validators.required, Validators.email]],
phone: ['', [Validators.required, germanPhoneValidator()]],
passwordGroup: this.fb.group({
password: ['', [
Validators.required,
Validators.minLength(8),
// Komplexitäts-Validator könnte hier hinzugefügt werden
]],
confirmPassword: ['', Validators.required]
}, { validators: matchingPasswordsValidator('password', 'confirmPassword') })
});
}
onSubmit() {
if (this.registrationForm.valid) {
console.log('Registrierung eingereicht:', this.registrationForm.value);
}
}
// Hilfsmethode, um einfacher auf verschachtelte Validierungsfehler zuzugreifen
hasPasswordMismatch() {
return this.registrationForm.get('passwordGroup')?.errors?.['passwordsMismatch'];
}
}Asynchrone Validatoren sind nützlich, wenn eine Validierung Serverinteraktionen erfordert, z.B. um zu prüfen, ob ein Benutzername bereits existiert:
// async-validators.service.ts
import { Injectable } from '@angular/core';
import { AbstractControl, AsyncValidatorFn, ValidationErrors } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { map, catchError, debounceTime, switchMap, first } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AsyncValidatorsService {
constructor(private http: HttpClient) {}
uniqueUsername(): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
if (!control.value) {
return of(null);
}
return of(control.value).pipe(
debounceTime(500), // Warten, bis der Benutzer aufhört zu tippen
switchMap(username =>
this.http.get<boolean>(`/api/check-username?username=${username}`).pipe(
map(isAvailable => isAvailable ? null : { usernameExists: true }),
catchError(() => of(null)) // Fehler abfangen und null zurückgeben
)
),
first() // Nur den ersten Wert nehmen und dann komplett sein
);
};
}
}Verwendung in einem Formular:
// registration-form.component.ts (erweitert)
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AsyncValidatorsService } from './async-validators.service';
@Component({
selector: 'app-registration-form',
templateUrl: './registration-form.component.html',
})
export class RegistrationFormComponent implements OnInit {
registrationForm!: FormGroup;
constructor(
private fb: FormBuilder,
private asyncValidators: AsyncValidatorsService
) {}
ngOnInit() {
this.registrationForm = this.fb.group({
username: [
'',
[Validators.required, Validators.minLength(4)],
[this.asyncValidators.uniqueUsername()]
],
// ... andere Felder ...
});
}
// ... Rest der Komponente ...
}Um Formulardaten an einen Server zu senden, verwenden Sie den HttpClient-Service:
// form-submission.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-form-submission',
templateUrl: './form-submission.component.html',
})
export class FormSubmissionComponent {
form: FormGroup;
submitting = false;
submitSuccess = false;
errorMessage = '';
constructor(
private fb: FormBuilder,
private http: HttpClient
) {
this.form = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
message: ['', Validators.required]
});
}
onSubmit() {
if (this.form.invalid) return;
this.submitting = true;
this.submitSuccess = false;
this.errorMessage = '';
this.http.post<any>('/api/contact', this.form.value)
.subscribe({
next: () => {
this.submitSuccess = true;
this.form.reset();
},
error: (error) => {
this.errorMessage = error.message || 'Ein Fehler ist aufgetreten.';
},
complete: () => {
this.submitting = false;
}
});
}
}<!-- form-submission.component.html -->
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<!-- Formularfelder -->
<button type="submit" [disabled]="form.invalid || submitting">
<ng-container *ngIf="submitting; else submitButton">
Wird gesendet...
</ng-container>
<ng-template #submitButton>
Absenden
</ng-template>
</button>
<div *ngIf="submitSuccess" class="success-message">
Vielen Dank! Ihre Nachricht wurde erfolgreich gesendet.
</div>
<div *ngIf="errorMessage" class="error-message">
{{ errorMessage }}
</div>
</form>Erstellen Sie wiederverwendbare Formularkomponenten:
// address-form.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-address-form',
templateUrl: './address-form.component.html',
})
export class AddressFormComponent implements OnInit {
@Input() parentForm!: FormGroup;
@Input() formGroupName = 'address';
constructor(private fb: FormBuilder) {}
ngOnInit() {
// Erstelle und füge die Adressgruppe zum übergeordneten Formular hinzu
const addressGroup = this.fb.group({
street: ['', Validators.required],
city: ['', Validators.required],
zipCode: ['', [Validators.required, Validators.pattern(/^\d{5}$/)]]
});
this.parentForm.addControl(this.formGroupName, addressGroup);
}
}Erstellen Sie einen Service zur Zentralisierung von Fehlermeldungen:
// validation-messages.service.ts
import { Injectable } from '@angular/core';
import { AbstractControl } from '@angular/forms';
@Injectable({
providedIn: 'root'
})
export class ValidationMessagesService {
getErrorMessage(control: AbstractControl, fieldName: string): string {
if (!control || !control.errors) {
return '';
}
const errors = control.errors;
if (errors['required']) {
return `${fieldName} ist erforderlich.`;
}
if (errors['email']) {
return `Bitte geben Sie eine gültige E-Mail-Adresse ein.`;
}
if (errors['minlength']) {
return `${fieldName} muss mindestens ${errors['minlength'].requiredLength} Zeichen lang sein.`;
}
if (errors['pattern']) {
return `${fieldName} hat ein ungültiges Format.`;
}
// Benutzerdefinierte Fehler
if (errors['passwordsMismatch']) {
return `Die Passwörter stimmen nicht überein.`;
}
// Fallback für unbekannte Fehler
return 'Ungültiger Wert.';
}
}Erstellen Sie eine Komponente zur Anzeige des Formularstatus (nützlich während der Entwicklung):
// form-debug.component.ts
import { Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-form-debug',
template: `
<div class="form-debug" *ngIf="showDebug">
<h3>Formular-Debug-Info</h3>
<pre>Formular-Status: {{ form.status }}</pre>
<pre>Formular gültig: {{ form.valid }}</pre>
<pre>Formular-Werte: {{ form.value | json }}</pre>
</div>
`,
styles: [`
.form-debug {
margin-top: 20px;
padding: 10px;
background-color: #f5f5f5;
border: 1px solid #ddd;
}
`]
})
export class FormDebugComponent {
@Input() form!: FormGroup;
@Input() showDebug = true; // In Produktionsumgebungen auf false setzen
}Angular bietet verbesserte Typisierung für Formulare, was die Typensicherheit und das Entwicklererlebnis verbessert:
// Typisiertes Reactive Form
interface UserForm {
name: string;
email: string;
address: {
street: string;
city: string;
zipCode: string;
};
}
// In der Komponente
const form = this.fb.group<UserForm>({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
address: this.fb.group({
street: ['', Validators.required],
city: ['', Validators.required],
zipCode: ['', Validators.required]
})
});
// Typensicherer Zugriff
const name = form.controls.name.value; // string
const city = form.controls.address.controls.city.value; // stringAngular bietet einen verbesserten FormBuilder mit besserer Typunterstützung:
// typensicherer FormBuilder
const userForm = this.fb.nonNullable.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
age: [0, [Validators.required, Validators.min(18)]]
});
// Hier wird ein FormControl<string> erzeugt, der niemals null sein kann
const nameControl = this.fb.nonNullable.control('');Angular bietet robuste und flexible Möglichkeiten zur Formularverarbeitung, sei es mit Template-driven Forms für einfachere Anwendungsfälle oder mit Reactive Forms für komplexere Szenarien.
Bei der Entscheidung zwischen Template-driven und Reactive Forms sollten Sie folgende Faktoren berücksichtigen:
Mit den in diesem Leitfaden vorgestellten Techniken und Beispielen sollten Sie in der Lage sein, effektive und benutzerfreundliche Formulare für Ihre Angular-Anwendungen zu erstellen.