Angular Reactive Forms
Zeit für professionelle Formulare! Reactive Forms sind die mächtigste Art, Forms in Angular zu bauen. 📝
📋 In diesem Artikel lernst du:
- Template-Driven vs Reactive Forms
- Reactive Forms Setup
- FormControl, FormGroup und FormBuilder
- Form Validation (Built-in & Custom)
- Dynamic Forms erstellen
- Form Arrays für mehrere Inputs
- Cross-Field Validation
- Async Validators
- Best Practices für robuste Forms
Template-Driven vs Reactive Forms
Angular bietet zwei Arten von Forms:
Template-Driven Forms
Logik im Template (HTML):
<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm)">
<input
type="email"
name="email"
[(ngModel)]="email"
required
email
>
<button [disabled]="!loginForm.valid">Login</button>
</form>
Vorteile:
- ✅ Einfach für simple Forms
- ✅ Wenig TypeScript Code
Nachteile:
- ❌ Schwer zu testen
- ❌ Wenig Kontrolle
- ❌ Komplex bei großen Forms
Reactive Forms ⭐
Logik im TypeScript:
export class LoginComponent {
loginForm = new FormGroup({
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required])
});
onSubmit() {
if (this.loginForm.valid) {
console.log(this.loginForm.value);
}
}
}
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<input type="email" formControlName="email">
<input type="password" formControlName="password">
<button [disabled]="!loginForm.valid">Login</button>
</form>
Vorteile:
- ✅ Type-safe (mit TypeScript)
- ✅ Leicht zu testen
- ✅ Volle Kontrolle über Form State
- ✅ Skaliert gut für komplexe Forms
- ✅ Dynamic Forms möglich
Nutze Reactive Forms für alles außer simpelste Forms! ✅
Reactive Forms Setup
ReactiveFormsModule importieren
app.module.ts:
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule // ✅ Import hinzufügen
],
// ...
})
export class AppModule { }
Ohne diesen Import funktionieren Reactive Forms NICHT!
FormControl - Einzelnes Input Field
FormControl repräsentiert ein einzelnes Form Field.
Basic Example
login.component.ts:
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-login',
template: `
<div class="form-field">
<label>Email:</label>
<input type="email" [formControl]="emailControl">
<p>Aktueller Wert: {{ emailControl.value }}</p>
</div>
`
})
export class LoginComponent {
emailControl = new FormControl(''); // Initial Value: ''
}
Erklärung:
new FormControl('')- Erstellt Control mit initial value[formControl]="emailControl"- Bindet Input an ControlemailControl.value- Aktueller Wert
FormControl mit Initial Value
emailControl = new FormControl('john@example.com'); // Prefilled
FormControl State
constructor() {
console.log(this.emailControl.value); // Wert
console.log(this.emailControl.valid); // Ist valide?
console.log(this.emailControl.invalid); // Ist invalide?
console.log(this.emailControl.touched); // Wurde angefasst?
console.log(this.emailControl.dirty); // Wurde geändert?
console.log(this.emailControl.pristine); // Noch unverändert?
}
Wert setzen/updaten
// Wert setzen
this.emailControl.setValue('new@example.com');
// Wert patchen (alternative)
this.emailControl.patchValue('new@example.com');
// Reset (zurück zu initial value)
this.emailControl.reset();
FormGroup - Mehrere Fields gruppieren
FormGroup bündelt mehrere FormControls.
Basic Login Form
login.component.ts:
import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
loginForm = new FormGroup({
email: new FormControl('', [
Validators.required,
Validators.email
]),
password: new FormControl('', [
Validators.required,
Validators.minLength(6)
])
});
onSubmit() {
if (this.loginForm.valid) {
console.log('Form Data:', this.loginForm.value);
// { email: 'john@example.com', password: '123456' }
} else {
console.log('Form is invalid!');
}
}
get email() {
return this.loginForm.get('email');
}
get password() {
return this.loginForm.get('password');
}
}
login.component.html:
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="login-form">
<h2>Login</h2>
<!-- Email Field -->
<div class="form-field">
<label>Email:</label>
<input
type="email"
formControlName="email"
[class.error]="email?.invalid && email?.touched"
>
<div *ngIf="email?.invalid && email?.touched" class="error-message">
<p *ngIf="email?.errors?.['required']">Email ist erforderlich</p>
<p *ngIf="email?.errors?.['email']">Keine gültige Email-Adresse</p>
</div>
</div>
<!-- Password Field -->
<div class="form-field">
<label>Passwort:</label>
<input
type="password"
formControlName="password"
[class.error]="password?.invalid && password?.touched"
>
<div *ngIf="password?.invalid && password?.touched" class="error-message">
<p *ngIf="password?.errors?.['required']">Passwort ist erforderlich</p>
<p *ngIf="password?.errors?.['minlength']">
Mindestens {{ password?.errors?.['minlength'].requiredLength }} Zeichen
</p>
</div>
</div>
<!-- Submit Button -->
<button
type="submit"
[disabled]="!loginForm.valid"
>
Login
</button>
<!-- Debug Info -->
<div class="debug">
<p>Form Valid: {{ loginForm.valid }}</p>
<p>Form Value: {{ loginForm.value | json }}</p>
</div>
</form>
login.component.css:
.login-form {
max-width: 400px;
margin: 2rem auto;
padding: 2rem;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
.form-field {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}
input {
width: 100%;
padding: 0.75rem;
border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 1rem;
}
input.error {
border-color: #ef4444;
}
.error-message {
color: #ef4444;
font-size: 0.875rem;
margin-top: 0.25rem;
}
button {
width: 100%;
padding: 0.75rem;
background: #3b82f6;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
}
button:disabled {
background: #9ca3af;
cursor: not-allowed;
}
.debug {
margin-top: 1rem;
padding: 1rem;
background: #f3f4f6;
border-radius: 4px;
font-size: 0.875rem;
}
Erklärung:
[formGroup]="loginForm"- Bindet Form an FormGroupformControlName="email"- Bindet Input an Control im FormGrouploginForm.value- Alle Form Werte als ObjektloginForm.valid- Ist gesamtes Form valide?email?.invalid && email?.touched- Zeige Fehler nur wenn touched
Built-in Validators
Angular bietet viele Built-in Validators:
Alle Built-in Validators
import { Validators } from '@angular/forms';
new FormControl('', [
Validators.required, // Pflichtfeld
Validators.email, // Email Format
Validators.min(18), // Minimum Zahl
Validators.max(100), // Maximum Zahl
Validators.minLength(6), // Mindestlänge
Validators.maxLength(20), // Maximallänge
Validators.pattern(/^[a-zA-Z]+$/) // Regex Pattern
]);
Beispiele
Nur Zahlen:
ageControl = new FormControl('', [
Validators.required,
Validators.min(18),
Validators.max(120)
]);
Username (alphanumerisch, 3-15 Zeichen):
usernameControl = new FormControl('', [
Validators.required,
Validators.minLength(3),
Validators.maxLength(15),
Validators.pattern(/^[a-zA-Z0-9_]+$/)
]);
Phone Number (Deutsche Handynummer):
phoneControl = new FormControl('', [
Validators.required,
Validators.pattern(/^(\+49|0)[1-9][0-9]{9,10}$/)
]);
Tipps & Tricks
Validator Error Messages
Error Object auslesen:
// Password Control Errors:
password?.errors
// → { required: true } oder
// → { minlength: { requiredLength: 6, actualLength: 3 } }
Im Template nutzen:
<div *ngIf="password?.errors?.['minlength']">
Mindestens {{ password?.errors?.['minlength'].requiredLength }} Zeichen
(aktuell: {{ password?.errors?.['minlength'].actualLength }})
</div>
Getter für Controls
Statt:
<div *ngIf="loginForm.get('email')?.invalid">
Nutze Getter:
get email() {
return this.loginForm.get('email');
}
<div *ngIf="email?.invalid">
Viel lesbarer! ✅
Touched vs Dirty vs Pristine
touched: User hat Feld fokussiert (geklickt) untouched: Feld noch nie fokussiert
dirty: Wert wurde geändert pristine: Noch Original-Wert
Zeige Fehler nur wenn touched:
<div *ngIf="email?.invalid && email?.touched">
<!-- Fehler nur nach Fokus-Verlust -->
</div>
FormBuilder - Forms schneller erstellen
FormBuilder ist ein Service der Forms einfacher macht!
Ohne FormBuilder
loginForm = new FormGroup({
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required])
});
Mit FormBuilder ✅
import { FormBuilder } from '@angular/forms';
constructor(private fb: FormBuilder) {}
loginForm = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required]]
});
Viel kürzer und lesbarer!
Komplexes Beispiel: Registration Form
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-register',
templateUrl: './register.component.html'
})
export class RegisterComponent {
registerForm = this.fb.group({
// Personal Info
firstName: ['', [Validators.required, Validators.minLength(2)]],
lastName: ['', [Validators.required, Validators.minLength(2)]],
email: ['', [Validators.required, Validators.email]],
// Password
password: ['', [
Validators.required,
Validators.minLength(8),
Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/) // Min 1 lowercase, 1 uppercase, 1 digit
]],
confirmPassword: ['', [Validators.required]],
// Address
address: this.fb.group({
street: ['', Validators.required],
city: ['', Validators.required],
zip: ['', [Validators.required, Validators.pattern(/^\d{5}$/)]],
country: ['DE', Validators.required]
}),
// Terms
agreeToTerms: [false, Validators.requiredTrue]
});
constructor(private fb: FormBuilder) {}
onSubmit() {
if (this.registerForm.valid) {
console.log('Registration Data:', this.registerForm.value);
}
}
// Getter
get firstName() { return this.registerForm.get('firstName'); }
get lastName() { return this.registerForm.get('lastName'); }
get email() { return this.registerForm.get('email'); }
get password() { return this.registerForm.get('password'); }
get confirmPassword() { return this.registerForm.get('confirmPassword'); }
get address() { return this.registerForm.get('address'); }
get agreeToTerms() { return this.registerForm.get('agreeToTerms'); }
}
Nested FormGroup: address ist ein eigenes FormGroup!
Custom Validators
Erstelle eigene Custom Validators für spezielle Validierungen!
Simple Custom Validator
validators/custom-validators.ts:
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export class CustomValidators {
// Nur Buchstaben (keine Zahlen/Sonderzeichen)
static onlyLetters(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (!value) {
return null; // Leer ist OK (nutze Validators.required für Pflichtfeld)
}
const valid = /^[a-zA-ZäöüÄÖÜß\s]+$/.test(value);
return valid ? null : { onlyLetters: true };
};
}
// Keine Leerzeichen erlaubt
static noWhitespace(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (!value) {
return null;
}
const hasWhitespace = /\s/.test(value);
return hasWhitespace ? { noWhitespace: true } : null;
};
}
// Starkes Passwort (min 8 chars, 1 uppercase, 1 lowercase, 1 digit, 1 special char)
static strongPassword(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (!value) {
return null;
}
const hasUpperCase = /[A-Z]/.test(value);
const hasLowerCase = /[a-z]/.test(value);
const hasDigit = /\d/.test(value);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value);
const isLongEnough = value.length >= 8;
const valid = hasUpperCase && hasLowerCase && hasDigit && hasSpecialChar && isLongEnough;
if (valid) {
return null;
}
return {
strongPassword: {
hasUpperCase,
hasLowerCase,
hasDigit,
hasSpecialChar,
isLongEnough
}
};
};
}
}
Custom Validator nutzen
import { CustomValidators } from './validators/custom-validators';
registerForm = this.fb.group({
firstName: ['', [
Validators.required,
CustomValidators.onlyLetters() // ✅ Custom Validator
]],
username: ['', [
Validators.required,
CustomValidators.noWhitespace()
]],
password: ['', [
Validators.required,
CustomValidators.strongPassword()
]]
});
Error Messages:
<div *ngIf="firstName?.errors?.['onlyLetters']">
Nur Buchstaben erlaubt
</div>
<div *ngIf="password?.errors?.['strongPassword'] as error">
<p>Passwort muss enthalten:</p>
<ul>
<li [class.valid]="error.hasUpperCase">✓ Großbuchstabe</li>
<li [class.valid]="error.hasLowerCase">✓ Kleinbuchstabe</li>
<li [class.valid]="error.hasDigit">✓ Zahl</li>
<li [class.valid]="error.hasSpecialChar">✓ Sonderzeichen</li>
<li [class.valid]="error.isLongEnough">✓ Min. 8 Zeichen</li>
</ul>
</div>
Cross-Field Validation
Cross-Field Validators validieren mehrere Fields zusammen.
Beispiel: "Password" und "Confirm Password" müssen identisch sein.
Password Match Validator
validators/password-match.validator.ts:
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export function passwordMatchValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const password = control.get('password');
const confirmPassword = control.get('confirmPassword');
if (!password || !confirmPassword) {
return null;
}
// Passwörter stimmen nicht überein
if (password.value !== confirmPassword.value) {
confirmPassword.setErrors({ passwordMismatch: true });
return { passwordMismatch: true };
}
// Passwörter stimmen überein → Error entfernen
const errors = confirmPassword.errors;
if (errors) {
delete errors['passwordMismatch'];
confirmPassword.setErrors(Object.keys(errors).length > 0 ? errors : null);
}
return null;
};
}
Validator auf FormGroup anwenden
registerForm = this.fb.group({
password: ['', [Validators.required, Validators.minLength(8)]],
confirmPassword: ['', [Validators.required]]
}, {
validators: [passwordMatchValidator()] // ✅ Form-Level Validator
});
Error Message:
<div *ngIf="confirmPassword?.errors?.['passwordMismatch']">
Passwörter stimmen nicht überein
</div>
Häufige Fehler
Fehler 1: ReactiveFormsModule nicht importiert
❌ Problem:
Error: Can't bind to 'formGroup' since it isn't a known property of 'form'
✅ Lösung:
// app.module.ts
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [ReactiveFormsModule] // ✅ Import hinzufügen
})
Fehler 2: formControlName ohne formGroup
❌ Problem:
<form>
<input formControlName="email"> <!-- ❌ Fehlt [formGroup] -->
</form>
✅ Lösung:
<form [formGroup]="loginForm"> <!-- ✅ formGroup hinzufügen -->
<input formControlName="email">
</form>
Fehler 3: setValue vs patchValue
❌ Problem:
// Form hat 3 Fields: email, password, rememberMe
this.loginForm.setValue({ email: 'test@test.com' });
// ❌ Error! setValue braucht ALLE Fields!
✅ Lösung 1: Alle Werte
this.loginForm.setValue({
email: 'test@test.com',
password: '',
rememberMe: false
});
✅ Lösung 2: patchValue (partial)
this.loginForm.patchValue({
email: 'test@test.com' // ✅ Andere Fields bleiben unverändert
});
Fehler 4: Fehler zu früh anzeigen
❌ Problem:
<!-- Fehler sofort sichtbar, auch wenn User noch gar nicht getippt hat -->
<div *ngIf="email?.invalid">Error!</div>
✅ Lösung:
<!-- Fehler nur nach Touch -->
<div *ngIf="email?.invalid && email?.touched">Error!</div>
<!-- Oder nach Submit Versuch -->
<div *ngIf="email?.invalid && (email?.touched || submitted)">Error!</div>
Dynamic Forms
Dynamische Forms ändern sich basierend auf User-Input!
Beispiel: Conditional Fields
export class DynamicFormComponent implements OnInit {
form = this.fb.group({
accountType: ['personal', Validators.required],
firstName: ['', Validators.required],
lastName: ['', Validators.required],
// Company Fields (nur wenn accountType = 'business')
companyName: [''],
vatNumber: ['']
});
constructor(private fb: FormBuilder) {}
ngOnInit() {
// ✅ Watch accountType changes
this.form.get('accountType')?.valueChanges.subscribe(type => {
const companyName = this.form.get('companyName');
const vatNumber = this.form.get('vatNumber');
if (type === 'business') {
// Business Account → Company Fields required
companyName?.setValidators([Validators.required]);
vatNumber?.setValidators([Validators.required]);
} else {
// Personal Account → Company Fields optional
companyName?.clearValidators();
vatNumber?.clearValidators();
}
// ✅ Validators updaten
companyName?.updateValueAndValidity();
vatNumber?.updateValueAndValidity();
});
}
get isBusinessAccount() {
return this.form.get('accountType')?.value === 'business';
}
}
Template:
<form [formGroup]="form">
<select formControlName="accountType">
<option value="personal">Privat</option>
<option value="business">Geschäftlich</option>
</select>
<input formControlName="firstName" placeholder="Vorname">
<input formControlName="lastName" placeholder="Nachname">
<!-- ✅ Conditional Fields -->
<div *ngIf="isBusinessAccount">
<input formControlName="companyName" placeholder="Firmenname">
<input formControlName="vatNumber" placeholder="USt-IdNr.">
</div>
</form>
FormArray - Dynamische Liste von Controls
FormArray für mehrere gleichartige Inputs (z.B. mehrere Email-Adressen).
Beispiel: Multiple Email Addresses
import { Component } from '@angular/core';
import { FormBuilder, FormArray, Validators } from '@angular/forms';
@Component({
selector: 'app-emails',
template: `
<form [formGroup]="form">
<h3>Email-Adressen</h3>
<div formArrayName="emails">
<div *ngFor="let email of emails.controls; let i = index" class="email-row">
<input
[formControlName]="i"
placeholder="Email {{ i + 1 }}"
>
<button type="button" (click)="removeEmail(i)">Entfernen</button>
</div>
</div>
<button type="button" (click)="addEmail()">+ Email hinzufügen</button>
<div class="debug">
<p>Emails: {{ emails.value | json }}</p>
</div>
</form>
`
})
export class EmailsComponent {
form = this.fb.group({
emails: this.fb.array([
this.fb.control('', [Validators.required, Validators.email])
])
});
constructor(private fb: FormBuilder) {}
get emails() {
return this.form.get('emails') as FormArray;
}
addEmail() {
this.emails.push(
this.fb.control('', [Validators.required, Validators.email])
);
}
removeEmail(index: number) {
this.emails.removeAt(index);
}
}
Erklärung:
FormArray- Liste von ControlsformArrayName="emails"- Bindet Array[formControlName]="i"- Bindet Control per Indexemails.push(...)- Neues Control hinzufügenemails.removeAt(i)- Control entfernen
Debugging & Troubleshooting
Form Value debuggen
Im Template:
<pre>{{ form.value | json }}</pre>
<pre>{{ form.status }}</pre>
Im TypeScript:
constructor() {
// ✅ Watch alle Changes
this.form.valueChanges.subscribe(value => {
console.log('Form Value:', value);
});
// ✅ Watch Status Changes
this.form.statusChanges.subscribe(status => {
console.log('Form Status:', status); // VALID, INVALID, PENDING
});
// ✅ Watch einzelnes Field
this.form.get('email')?.valueChanges.subscribe(value => {
console.log('Email changed:', value);
});
}
Validation Errors finden
onSubmit() {
if (this.form.invalid) {
console.log('Form is invalid!');
// ✅ Alle Errors loggen
Object.keys(this.form.controls).forEach(key => {
const control = this.form.get(key);
if (control?.invalid) {
console.log(`${key} errors:`, control.errors);
}
});
}
}
FormArray Index Probleme
Problem: [formControlName]="i" funktioniert nicht
✅ Check:
- Hast du
formArrayName="emails"am Parent? - Nutzt du
let i = indexin*ngFor? - Ist
[formControlName]mit eckigen Klammern (Property Binding)?
<!-- ✅ CORRECT -->
<div formArrayName="emails">
<div *ngFor="let control of emails.controls; let i = index">
<input [formControlName]="i">
</div>
</div>
Async Validators
Async Validators für Validierungen die API-Calls brauchen.
Beispiel: Prüfe ob Username bereits existiert.
Username Availability Validator
validators/username-async.validator.ts:
import { AbstractControl, AsyncValidatorFn, ValidationErrors } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { map, delay } from 'rxjs/operators';
export function usernameAvailableValidator(): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
const username = control.value;
if (!username) {
return of(null);
}
// Simuliere API Call (in Realität: HttpClient)
const takenUsernames = ['admin', 'john', 'test', 'user'];
const isTaken = takenUsernames.includes(username.toLowerCase());
return of(isTaken).pipe(
delay(1000), // Simuliere Netzwerk-Delay
map(taken => taken ? { usernameTaken: true } : null)
);
};
}
Async Validator nutzen
form = this.fb.group({
username: [
'',
[Validators.required], // Sync Validators
[usernameAvailableValidator()] // Async Validators (3. Parameter!)
]
});
Template:
<input formControlName="username">
<!-- ✅ Pending State (während API Call) -->
<p *ngIf="username?.pending">Prüfe Verfügbarkeit...</p>
<!-- ✅ Error Message -->
<p *ngIf="username?.errors?.['usernameTaken']">
Username ist bereits vergeben
</p>
Wichtig: Form Status ist PENDING während Async Validator läuft!
Zusammenfassung
Du hast gelernt:
- ✅ Reactive Forms sind mächtiger als Template-Driven Forms
- ✅
ReactiveFormsModuleimportieren - ✅ FormControl für einzelne Fields
- ✅ FormGroup um mehrere Controls zu gruppieren
- ✅ FormBuilder macht Code kürzer
- ✅ Built-in Validators: required, email, minLength, pattern, etc.
- ✅ Custom Validators für eigene Validierungslogik
- ✅ Cross-Field Validators für Validierung über mehrere Fields
- ✅ FormArray für dynamische Listen
- ✅ Async Validators für API-basierte Validierung
- ✅
touched&dirtyfür bessere UX bei Fehlern
Key Takeaways:
- Reactive Forms = Type-safe, testbar, skalierbar
- FormBuilder für cleanen Code
- Zeige Errors nur nach
touched - Custom Validators für komplexe Logik
- valueChanges Observable für Reactive Updates
Form Setup Pattern:
form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]]
});
get email() { return this.form.get('email'); }
onSubmit() {
if (this.form.valid) {
console.log(this.form.value);
}
}
Reactive Forms sind der Standard für professionelle Angular Apps! 🎯
Quiz
📝 Quiz
Was ist der Hauptvorteil von Reactive Forms?
📝 Quiz
Wofür nutzt man FormBuilder?
📝 Quiz
Wann sollte man Fehler anzeigen?
Übungen
Übung 1: Contact Form
Erstelle ein Kontaktformular mit:
- Name (required, min 2 chars)
- Email (required, valid email)
- Subject (required, dropdown: "Support", "Sales", "Other")
- Message (required, min 10 chars, max 500 chars)
- Subscribe to Newsletter (checkbox)
Zeige alle Errors nur nach touched. Disable Submit Button bis Form valid.
Übung 2: Registration Form mit Password Match
Erstelle Registration Form mit:
- Username (required, 3-20 chars, no whitespace)
- Email (required, valid email)
- Password (required, min 8 chars, strong password validator)
- Confirm Password (required, must match password)
- Terms Checkbox (required)
Implementiere Custom Validator für Password Match.
Übung 3: Dynamic Skill Form
Erstelle Form wo User mehrere Skills hinzufügen kann:
- Jeder Skill hat: Name (text) + Level (1-5 stars)
- "+ Skill hinzufügen" Button
- "Entfernen" Button pro Skill
- Mindestens 1 Skill erforderlich
Nutze FormArray!
Nächste Schritte
Im nächsten Artikel:
- HTTP Client
- API Requests (GET, POST, PUT, DELETE)
- Error Handling
- Interceptors
- RxJS Operators für HTTP
➡️ Weiter zu: Angular HTTP & APIs
Forms gemeistert! Zeit für echte Daten von APIs! 🚀
Gut gemacht! 🎉
Du hast "Angular Reactive Forms - Formulare professionell erstellen" abgeschlossen
Artikel bewerten
Bitte einloggen um zu bewerten
Das könnte dich auch interessieren
Angular HTTP Client & APIs - Backend-Kommunikation
Lerne HTTP Requests mit Angular HttpClient: GET, POST, PUT, DELETE, Error Handling, Interceptors und RxJS Operators für professionelle API-Integration.
Angular Routing & Navigation - Multi-Page Apps erstellen
Lerne Angular Router kennen, erstelle Routes, navigiere zwischen Seiten, nutze Route Parameters und schütze Routes mit Guards.
Angular Services & Dependency Injection - Logik auslagern
Lerne Services in Angular zu erstellen, Dependency Injection zu verstehen und zentrale Geschäftslogik effizient zu organisieren.