Динамические формы — это формы, структура которых определяется во время выполнения приложения, а не на этапе компиляции. Они особенно полезны, когда:
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html'
})
export class DynamicFormComponent {
dynamicForm: FormGroup;
constructor(private fb: FormBuilder) {
this.dynamicForm = this.fb.group({
personalInfo: this.fb.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required]
}),
addresses: this.fb.array([]) // Динамический массив адресов
});
}
addAddress() {
const addressGroup = this.fb.group({
street: ['', Validators.required],
city: ['', Validators.required],
zip: ['', [Validators.required, Validators.pattern(/^\d{5}$/)]]
});
this.addresses.push(addressGroup);
}
get addresses(): FormArray {
return this.dynamicForm.get('addresses') as FormArray;
}
}
Шаблон для такого подхода:
<form [formGroup]="dynamicForm">
<div formGroupName="personalInfo">
<input formControlName="firstName" placeholder="First Name">
<input formControlName="lastName" placeholder="Last Name">
</div>
<div formArrayName="addresses">
<div *ngFor="let address of addresses.controls; let i=index" [formGroupName]="i">
<input formControlName="street" placeholder="Street">
<input formControlName="city" placeholder="City">
<input formControlName="zip" placeholder="ZIP Code">
</div>
<button type="button" (click)="addAddress()">Add Address</button>
</div>
</form>
interface FieldConfig {
type: string;
name: string;
label?: string;
value?: any;
validators?: ValidatorFn[];
}
@Component({
selector: 'app-configurable-form',
templateUrl: './configurable-form.component.html'
})
export class ConfigurableFormComponent {
form: FormGroup;
fields: FieldConfig[] = [];
fieldTypes = ['text', 'email', 'password', 'number'];
constructor(private fb: FormBuilder) {
this.loadFormConfig();
}
loadFormConfig() {
// Обычно конфиг загружается с сервера
this.fields = [
{ type: 'text', name: 'username', label: 'Username', validators: [Validators.required] },
{ type: 'email', name: 'email', label: 'Email', validators: [Validators.required, Validators.email] },
{ type: 'password', name: 'password', label: 'Password', validators: [Validators.required, Validators.minLength(8)] }
];
this.createForm();
}
createForm() {
const group = {};
this.fields.forEach(field => {
group[field.name] = [field.value || '', field.validators || []];
});
this.form = this.fb.group(group);
}
addField() {
this.fields.push({ type: 'text', name: `field_${Date.now()}`, label: 'New Field' });
this.createForm();
}
}
Шаблон для динамического отображения полей:
<form [formGroup]="form">
<div *ngFor="let field of fields">
<label>{{field.label || field.name}}</label>
<input *ngIf="field.type === 'text' || field.type === 'email' || field.type === 'password'"
[type]="field.type"
[formControlName]="field.name"
[placeholder]="field.label">
<input *ngIf="field.type === 'number'"
type="number"
[formControlName]="field.name"
[placeholder]="field.label">
<div *ngIf="form.get(field.name).invalid && form.get(field.name).touched">
Ошибка валидации
</div>
</div>
<button type="button" (click)="addField()">Add Field</button>
</form>
Для сложных случаев можно создавать компоненты форм динамически:
@Component({
selector: 'app-dynamic-form-container',
template: `<ng-container #formContainer></ng-container>`
})
export class DynamicFormContainerComponent implements OnInit {
@ViewChild('formContainer', {read: ViewContainerRef}) container: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) {}
ngOnInit() {
this.loadFormComponents();
}
async loadFormComponents() {
const config = await this.getFormConfigFromServer();
config.sections.forEach(section => {
const factory = this.resolver.resolveComponentFactory(FormSectionComponent);
const componentRef = this.container.createComponent(factory);
componentRef.instance.config = section;
});
}
}
updateValidator(fieldName: string, validators: ValidatorFn[]) {
const control = this.form.get(fieldName);
control.setValidators(validators);
control.updateValueAndValidity();
}
setCrossFieldValidation() {
this.form.setValidators((group: FormGroup) => {
const pass = group.get('password').value;
const confirmPass = group.get('confirmPassword').value;
return pass === confirmPass ? null : { mismatch: true };
});
}
ChangeDetectionStrategy.OnPush
Динамические формы открывают возможности для создания гибких, адаптивных интерфейсов, которые могут меняться без перекомпиляции приложения.