Angular 17 - 如何将 errorStateMatcher 添加到表单数组,但要以连续索引遍历 formGroups

问题描述 投票:0回答:1

以下代码有效,

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
    )
  );
}
angular typescript angular-reactive-forms formarray formgroups
1个回答
1
投票

首先,想澄清一下

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>
/容器才能完全显示错误消息。

演示@StackBlitz

.container {
  display: block;
  height: 150px;
  overflow: visible;
}
© www.soinside.com 2019 - 2024. All rights reserved.