这适用于一个简单的表单,只有一个开始和结束日期,但现在我的表单是动态的,因此有多对开始和结束日期,所以我必须使用表单数组。
这是结构,但现在我无法让 errorMatchers 或每个 FormGroup(在 FormArray 内)上的验证工作
/* error state matchers */
readonly startDateAfterEndDateMatchers: SingleErrorStateMatcher[] = [];
/* lifecycle hooks */
protected ngOnInit(): void {
// initialisation
this.initFormGroup();
this.formGetters = this.initFormGetters(this.datesInfo);
// create error state matchers
for (let i = 0; i < this.datesArray.length; i++) {
this.startDateAfterEndDateMatchers.push(
new SingleErrorStateMatcher('startDateAfterEndDate')
);
}
}
// INITIALISE FORM
private initFormGroup() {
this.datesInfo = this.formBuilder.group({
datesArray: this.formBuilder.array(
(this.datesArray || []).map((_) =>
this.formBuilder.group(
{
startDate: [
'',
{
nonNullable: true,
validators: [Validators.required],
},
],
endDate: [
'',
{
validators: [],
},
],
},
{ validators: [this.startDateAfterEndDateMatcher] }
)
)
),
});
}
这也是 stackblitz(它使用 Angular Material 2 和 3 组件) 任何帮助将不胜感激:https://stackblitz.com/edit/stackblitz-starters-ss9qeg?file=src%2Fmain.ts
提前致谢,
ST
您的
ErrorStateMatcher
由于这种情况而无法工作的原因:
formGroup?.hasError(this.errorCode)
返回
false
。相信来自 formGroup
方法的 isErrorState
将获得带有 FormGroup
标签的根 <form>
,而不是当前表单控件的父表单组。
isErrorState(
control: FormControl | null,
formGroup: FormGroupDirective | NgForm | null
)
因此,您需要自定义
ErrorStateMatcher
以提供当前 FormGroup
的父级 FormControl
。
export class SingleErrorStateMatcher implements ErrorStateMatcher {
private errorCode: string;
public constructor(errorCode: string, private formGroup?: FormGroup) {
this.errorCode = errorCode;
}
isErrorState(
control: FormControl | null,
formGroup: FormGroupDirective | NgForm | null
): boolean {
let parentFormGroup = this.formGroup ?? formGroup;
return (
!!(parentFormGroup?.dirty || parentFormGroup?.touched) &&
!!(parentFormGroup?.invalid && parentFormGroup?.hasError(this.errorCode))
);
}
}
以及当前将
SingleErrorStateMatcher
推入 startDateAfterEndDateMatchers
数组的方法。
for (let i = 0; i < this.datesArray.length; i++) {
this.startDateAfterEndDateMatchers.push(
new SingleErrorStateMatcher(
'startDateAfterEndDate',
this.datesInfo.controls['datesArray'].get(`${i}`) as FormGroup
)
);
}
此外,根据您的 HTML,您可以创建 3 个独立的
FormGroup
实例。
您的 HTML 结构应如下所示:
<form [formGroup]="datesInfo" class="form-group">
<!-- dates array -->
<div formArrayName="datesArray">
@for (date of datesArray; track $index) {
<ng-container [formGroupName]="$index">
<!-- start date -->
<mat-form-field class="form-date">
<!-- label -->
<mat-label> Start Date </mat-label>
<!-- input -->
<input
matInput
id="startDate-{{$index}}"
[matDatepicker]="startDatePicker"
[errorStateMatcher]="startDateAfterEndDateMatchers[$index]"
[formControl]="formGetters[$index]!.startDate"
autocomplete="off"
readonly
required
/>
<!-- hint -->
<mat-hint>DD/MM/YYYY</mat-hint>
<!-- picker -->
<mat-datepicker-toggle
matIconSuffix
[for]="startDatePicker"
[disabled]="false"
></mat-datepicker-toggle>
<mat-datepicker
#startDatePicker
[startAt]="(formGetters[$index]?.startDate?.value ?? null)"
></mat-datepicker>
<!-- errors -->
<mat-error
*ngIf="formGetters[$index]?.startDate?.invalid
&& (formGetters[$index]?.startDate?.dirty || formGetters[$index]?.startDate?.touched)"
>
@if(formGetters[$index]?.startDate?.errors?.['required']) {
Start Date is required.
}
</mat-error>
<mat-error
*ngIf="datesInfo.get('datesArray')!.get([$index])?.hasError('startDateAfterEndDate')"
>
Cannot be after End Date
</mat-error>
</mat-form-field>
<!-- end date -->
<mat-form-field class="form-date">
<!-- label -->
<mat-label> End Date </mat-label>
<!-- input -->
<input
(keydown)="endDatePicker.open()"
(click)="endDatePicker.open()"
matInput
id="endDate-{{$index}}"
[matDatepicker]="endDatePicker"
[errorStateMatcher]="startDateAfterEndDateMatchers[$index]"
[formControl]="formGetters[$index]!.endDate"
autocomplete="off"
/>
<!-- hint -->
<mat-hint>DD/MM/YYYY</mat-hint>
<!-- picker -->
<mat-datepicker-toggle
matIconSuffix
[for]="endDatePicker"
[disabled]="false"
></mat-datepicker-toggle>
<mat-datepicker
#endDatePicker
[startAt]="(formGetters[$index]?.endDate?.value ?? null)"
></mat-datepicker>
<!-- errors -->
<mat-error
*ngIf="formGetters[$index]?.endDate?.invalid
&& (formGetters[$index]?.endDate?.dirty || formGetters[$index]?.endDate?.touched)"
>
@if(formGetters[$index]?.endDate?.errors?.['required']) { End Date is required. }
</mat-error>
<mat-error
*ngIf="datesInfo.get('datesArray')!.get([$index])?.hasError('startDateAfterEndDate')"
>
Cannot be before Start Date
</mat-error>
</mat-form-field>
</ng-container>
}
</div>
</form>