Angular Forms
Motivation
<label>
Name: <input [(ngModel)]='name'>
</label>
<input [(ngModel)]='port' type='number'>
<button (click)='addEnvVar()'>+</button>
@for (envVar of envVars(); track $index) {
<input [(ngModel)]='envVar.name'>
<input [(ngModel)]='envVar.value'>
}
<button (click)='submit()'>Deploy</button>
- Validierung?
- Dynamisches Form?
- Komplexeres Form?
FormControl
name = new FormControl('');
<label>
Name: <input [formControl]='name'>
</label>
<p>Deploying {{ name.value }}</p>
- ein Input
- Two-Way-Binding
FormGroup
deployForm = new FormGroup({
'name': new FormControl(''),
'port': new FormControl(80)
});
<form [formGroup]='deployForm'>
<label for='name'>Name: </label>
<input id='name' formControlName='name'>
<label>
Port: <input formControlName='port' type='number'>
</label>
</form>
<p>{{ deployForm.value | json }}</p>
<!-- { 'name': 'backend', 'port': 80 } -->
- mehrere Inputs
Submit
<form [formGroup]='deployForm' (ngSubmit)='onSubmit()'>
...
<button type='submit' [disabled]='!deployForm.valid'>
Deploy
</button>
</form>
onSubmit() {
console.log(this.deployForm.value);
}
Setting/Updating
setValue() {
this.deployForm.setValue({
'name': '',
'port': 80
});
}
Setzt alle Werte
updateValue() {
this.deployForm.patchValue({
'name': ''
});
}
FormBuilder
deployForm = new FormGroup({
'name': new FormControl(''),
'port': new FormControl(80)
});
formBuilder = inject(FormBuilder);
deployForm = this.formBuilder.group({
'name': [''],
'port': [80]
});
form.reset() => null
formBuilder = inject(FormBuilder).nonNullable;
form.reset() => default
FormArray
deployForm = this.formBuilder.group({
...
'envVars': this.formBuilder.array([
this.formBuilder.group({
'name': ['application-name'],
'value': ['']
})
])
});
<form [formGroup]='deployForm' (ngSubmit)='onSubmit()'>
<div formArrayName='envVars'>
@for (envVar of deployForm.controls.envVars.controls; track $index) {
<div [formGroupName]='$index'>
<div>
<label for='env-name'>Name: </label>
<input id='env-name' formControlName='name'>
<label for='env-value'>Value: </label>
<input id='env-value' formControlName='value'>
</div>
</div>
}
</div>
Hinzufügen
addEnvVar() {
(this.deployForm.controls.envVars as FormArray).push(
this.formBuilder.group({
'name': [''],
'value': ['']
})
);
}
Bei leeren Arrays Typstellvertreter hilfreich
'envVars': this.formBuilder.array
<FormGroup<{ name: AbstractControl, value: AbstractControl }>>
([], {validators: this.unique()})
Validation
deployForm = this.formBuilder.group({
'name': ['', Validators.required],
'port': [80,
[ // synchronous Validators
Validators.required,
Validators.min(1),
Validators.max(65535),
this.validPort()
],
Validators.customAsync // asynchronous Validators
],
'envVars': this.formBuilder.array([], {validators: this.unique()})
}, {validators: this.globalValidation()});
validPort(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const port = control.value;
return [0, 443, 5432].find(p => p == port)
? {'Invalid port': port} // validation error
: null; // no error
};
}
CSS
.ng-touched:not(form,div) {
border-left: 5px solid blue;
}
Element war einmal im Focus
.ng-dirty:not(form) {
border-right: 5px solid green;
}
Element-value wurde verändert
.ng-invalid.ng-touched:not(form),
.ng-invalid.ng-dirty:not(form) {
border-left: 5px solid red;
}
Validator liefert Error
Error Messages
@if (deployForm.controls.port.invalid &&
(deployForm.controls.port.touched || deployForm.controls.port.dirty)) {
Error:
@if (deployForm.controls.port.errors?.['Invalid port']) {
Forbidden port
}
@if (deployForm.controls.port.errors?.['min']) {
Port must be positive
}
}