以下代码有效,
https://stackblitz.com/edit/stackblitz-starters-wnququ?file=src%2Fmain.html
但我需要更进一步,确保索引 (x) 处
FormGroup
的结束日期不大于或等于索引 (x + 1) 处 FormGroup
的开始日期 - (all在主窗体数组内)。
你知道我是怎么做的吗?
这就是我到目前为止所拥有的(也请参阅 Stackblitz 演示)
验证者
目前,我的日期验证器看起来像:
// VALIDATORS
public startDateAfterEndDateMatcher: ValidatorFn =
this.dateComparisonValidator(
'startDate',
'endDate',
'startDateAfterEndDate',
(date1: Date, date2: Date) => date1 && date2 && date1 > date2
);
private dateComparisonValidator(
fieldName1: string,
fieldName2: string,
errorName: string,
condition: (value1: any, value2: any) => boolean
): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const field1Value = control.get(fieldName1)?.value;
const field2Value = control.get(fieldName2)?.value;
console.log('condition', condition(field1Value, field2Value));
if (condition(field1Value, field2Value)) {
const errors: ValidationErrors = {};
errors[errorName] = true;
return errors;
}
return null;
};
}
表格结构
目前的表单结构如下所示。表单验证器被添加到每个 formGroup 对象上,(但我现在想尝试跨 formGroup 进行验证 - 我不知道该怎么做)
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] }
)
)
),
});
}
错误状态匹配器
我的错误状态匹配器(附加到表单数组中的每个表单组)如下所示:
// ERROR MATCHER
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;
console.log('parentFormGroup', parentFormGroup);
return (
!!(parentFormGroup?.dirty || parentFormGroup?.touched) &&
!!(parentFormGroup?.invalid && parentFormGroup?.hasError(this.errorCode))
);
}
}
初始化
这些仅被推入
ngOnInit
内部(因此从某种意义上说它不是完全动态的,我还没有考虑过如果我想添加另一对日期,或者如果我删除/回滚一对日期会发生什么) ... - 但现在没关系)
// create error state matchers
for (let i = 0; i < this.datesArray.length; i++) {
this.startDateAfterEndDateMatchers.push(
new SingleErrorStateMatcher(
'startDateAfterEndDate',
this.datesInfo.controls['datesArray'].get(`${i}`) as FormGroup
)
);
}
首先,想澄清一下
ErrorStateMatcher
用于如何/何时显示 <mat-error>
。所以你不应该将它与验证逻辑混合。
调整
SingleErrorStateMatcher
,当FormGroup
无效时显示错误而不是指定的错误(代码)。
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;
//console.log('parentFormGroup', parentFormGroup);
return (
!!(parentFormGroup?.dirty || parentFormGroup?.touched) &&
!!parentFormGroup?.invalid
);
}
}
为了将该字段与连续(下一个)字段进行比较,认为通过订阅
dateArray
FormArray
的 valuesChanges
可观察并添加验证会更容易。
subscription!: Subscription;
this.subscription = (
this.datesInfo.controls['datesArray'] as FormArray
).valueChanges.subscribe((dates) => {
dates.forEach((x: any, i: number) => {
const endDateExceedsStartDate = dates.some(
(y: any, j: number) =>
j == i + 1 && x.endDate && y.startDate && x.endDate >= y.startDate
);
const endDate = (
this.datesInfo.controls['datesArray']?.get(`${i}`) as FormGroup
)?.get('endDate')!;
if (endDateExceedsStartDate) {
endDate.setErrors(
{ endDateExceedsStartDate: true },
{ emitEvent: false }
);
} else {
if (endDate.hasError('endDateExceedsStartDate')) {
delete endDate.errors?.['endDateExceedsStartDate'];
endDate.updateValueAndValidity({ emitEvent: false });
}
}
});
});
并且不要忘记取消订阅以进行性能优化。
ngOnDestroy() {
this.subscription.unsubscribe();
}
显示“endDateExceedsStartDate”错误。
<mat-error
*ngIf="datesInfo.get('datesArray')!.get([$index])?.get('endDate')?.hasError('endDateExceedsStartDate')"
>
End Date cannot exceed (next) Start Date
</mat-error>
如果未显示
<mat-error>
,可能是由于空间不足。因此,您可能需要调整 <mat-form-field>
/容器才能完全显示错误消息。
.container {
display: block;
height: 150px;
overflow: visible;
}