mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3.5 KiB
3.5 KiB
Reactive Forms
Reactive forms provide a model-driven approach to handling form inputs. They are built around observable streams and provide synchronous access to the data model, making them more scalable and testable than template-driven forms.
Core Classes
Reactive forms are built using these fundamental classes from @angular/forms:
FormControl: Manages the value and validity of an individual input.FormGroup: Manages a group of controls (an object-like structure).FormArray: Manages a numerically indexed array of controls.FormBuilder: A service that provides factory methods for creating control instances.
Setup
Import ReactiveFormsModule into your component.
import {Component, inject} from '@angular/core';
import {ReactiveFormsModule, FormGroup, FormControl, Validators, FormBuilder} from '@angular/forms';
@Component({
selector: 'app-profile-editor',
imports: [ReactiveFormsModule],
templateUrl: './profile-editor.component.html',
})
export class ProfileEditor {
private fb = inject(FormBuilder);
// Using FormBuilder for concise definition
profileForm = this.fb.group({
firstName: ['', Validators.required],
lastName: [''],
address: this.fb.group({
street: [''],
city: [''],
}),
aliases: this.fb.array([this.fb.control('')]),
});
onSubmit() {
console.warn(this.profileForm.value);
}
}
Template Binding
Use directives to bind the model to the view:
[formGroup]: Binds aFormGroupto a<form>or<div>.formControlName: Binds a named control within a group to an input.formGroupName: Binds a nestedFormGroup.formArrayName: Binds a nestedFormArray.[formControl]: Binds a standaloneFormControl.
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<input type="text" formControlName="firstName" />
<div formGroupName="address">
<input type="text" formControlName="street" />
</div>
<div formArrayName="aliases">
@for (alias of aliases.controls; track $index) {
<input type="text" [formControlName]="$index" />
}
</div>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
Accessing Controls
Use getters for easy access to controls, especially for FormArray.
get aliases() {
return this.profileForm.get('aliases') as FormArray;
}
addAlias() {
this.aliases.push(this.fb.control(''));
}
Updating Values
patchValue(): Updates only the specified properties. Fails silently on structural mismatches.setValue(): Replaces the entire model. Strictly enforces the form structure.
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: { street: '123 Drew Street' }
});
}
Unified Change Events
Modern Angular (v18+) provides a single events observable on all controls to track value, status, pristine, touched, reset, and submit events.
import {ValueChangeEvent, StatusChangeEvent} from '@angular/forms';
this.profileForm.events.subscribe((event) => {
if (event instanceof ValueChangeEvent) {
console.log('New value:', event.value);
}
});
Manual State Management
markAsTouched()/markAllAsTouched(): Useful for showing validation errors on submit.markAsDirty()/markAsPristine(): Tracks if the value has been modified.updateValueAndValidity(): Manually triggers recalculation of value and status.- Options
{ emitEvent: false }or{ onlySelf: true }can be passed to most methods to control propagation.