Я создал повторно используемый компонент для создания / редактирования объекта моего приложения. Этот компонент содержит реактивную форму, которая имеет несколько динамических c частей.
Вся форма работает, как и ожидалось, но я столкнулся с проблемой с проверкой: каждый раз, когда я нажимаю кнопку, чтобы добавить динамическую c форму группа к форме, форма добавлена с указанием ошибок проверки. Кроме того, проверяется не только часть Dynami c, но и остальная часть формы.
Похоже, что при добавлении части Dynami c выполняется проверка, как если бы кнопка отправки была нажата, но это не так.
Чего я хочу добиться - это иметь возможность добавлять динамические c formGroups, без этого действия само выполняет проверку формы.
Я что-то пропустил точка?
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {AbstractControl, FormArray, FormControl, FormGroup, Validators} from '@angular/forms';
import {Plan} from '../shared/domain/plan.model';
import {Milestone} from '../shared/domain/milestone.model';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
@Input() plan?: Plan;
planForm = new FormGroup({
id: new FormControl(
null,
[
Validators.required,
Validators.minLength(4),
Validators.pattern('^[a-zA-Z0-9_-]+$')
]
),
milestones: new FormArray([])
});
constructor() {}
ngOnInit() {
this.fillForm();
}
fillForm() {
this.planForm.get('id').setValue(this.plan ? this.plan.getId() : '');
this.createMilestoneFormControls();
if (this.plan) {
this.planForm.get('id').disable();
}
}
createMilestoneFormControls() {
if (!this.plan) {
this.addMilestoneToFormArray();
return;
}
this.plan.getMilestones().forEach((milestone: Milestone) => {
this.addMilestoneToFormArray(milestone);
});
}
addMilestoneToFormArray(milestone?: Milestone) {
const milestonesFormArray = this.milestonesFormArray;
milestonesFormArray.push(new FormGroup({
description: new FormControl(milestone ? milestone.getDescription() : ''),
startDate: new FormControl(
milestone ? milestone.getStartDate().toISOString().substring(0, 10) : '',
[Validators.required]
),
endDate: new FormControl(
milestone ? milestone.getEndDate().toISOString().substring(0, 10) : '',
[Validators.required]
),
}));
}
get milestonesFormArray() {
return this.planForm.get('milestones') as FormArray;
}
removeMilestoneFromFormArray(i: number) {
this.milestonesFormArray.removeAt(i);
}
getValidationMessage(field: string, fault: string): string {
return this.validationMessages[field][fault];
}
getDateInputValidationMessage(fieldName: string, control: AbstractControl): string {
if (control.errors.matDatepickerMax) {
return this.getValidationMessage(fieldName, 'matDatepickerMax');
}
if (control.errors.matDatepickerParse) {
return this.getValidationMessage(fieldName, 'matDatepickerParse');
}
if (!control.value && control.errors.required) {
return this.getValidationMessage(fieldName, 'required');
}
return '';
}
}
<form [formGroup]="planForm" (keydown.enter)="$event.preventDefault()">
<mat-form-field class="formInput" appearance="outline">
<mat-label>Relationship identifier</mat-label>
<input matInput formControlName="id" autocomplete="off" required>
<mat-error *ngIf="planForm.get('id').invalid">
<div *ngIf="planForm.get('id').errors.required">{{getValidationMessage('id', 'required')}}</div>
<div *ngIf="planForm.get('id').errors.minlength">{{getValidationMessage('id', 'minlength')}}</div>
<div *ngIf="planForm.get('id').errors.pattern">{{getValidationMessage('id', 'pattern')}}</div>
</mat-error>
</mat-form-field>
<div formArrayName="milestones">
<div *ngFor="let milestone of milestonesFormArray.controls; let i=index"
[formGroupName]="i.toString()"
class="milestoneFormBlock">
<div class="mobile">Milestone #{{i+1}}</div>
<hr class="milestoneDivider">
<mat-form-field class="formInputMilestone" appearance="outline" style=" margin-bottom: -1.25em">
<mat-label>Description</mat-label>
<input matInput formControlName="description" autocomplete="off">
</mat-form-field>
<mat-form-field class="formInputMilestone" appearance="outline" style=" margin-bottom: -1.25em">
<mat-label>Start Date</mat-label>
<input
matInput
[matDatepicker]="milestoneStartDate"
formControlName="startDate"
autocomplete="off"
required
placeholder="mm/dd/yyyy"
[max]="milestone.get('endDate').value">
<mat-datepicker-toggle matSuffix [for]="milestoneStartDate"></mat-datepicker-toggle>
<mat-datepicker #milestoneStartDate></mat-datepicker>
<mat-error *ngIf="milestone.get('startDate').invalid">
<div>{{getDateInputValidationMessage('startDate', milestone.get('startDate'))}}</div>
</mat-error>
</mat-form-field>
<mat-form-field class="formInputMilestone" appearance="outline" style=" margin-bottom: -1.25em">
<mat-label>End Date</mat-label>
<input
matInput
[matDatepicker]="milestoneEndDate"
formControlName="endDate"
autocomplete="off"
required
placeholder="mm/dd/yyyy"
[min]="milestone.get('startDate').value">
<mat-datepicker-toggle matSuffix [for]="milestoneEndDate"></mat-datepicker-toggle>
<mat-datepicker #milestoneEndDate></mat-datepicker>
<mat-error *ngIf="milestone.get('endDate').invalid">
<div>{{getDateInputValidationMessage('endDate', milestone.get('endDate'))}}</div>
</mat-error>
</mat-form-field>
<button class="remove-milestone-btn" mat-raised-button color="warn" (click)="removeMilestoneFromFormArray(i)">
<fa-icon [icon]="['fas', 'trash']"></fa-icon>
</button>
</div>
<button class="add-milestone-btn" mat-fab color="primary" (click)="addMilestoneToFormArray()">
<fa-icon [icon]="['fas', 'plus']"></fa-icon>
</button>
</div>
<button class="submitBtn" (click)="onSubmit()" mat-raised-button color="primary">
<div *ngIf="plan; then thenBlock else elseBlock"></div>
<ng-template #thenBlock>Update relationship</ng-template>
<ng-template #elseBlock>Create relationship</ng-template>
</button>
</form>