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

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

您好,以下代码有效,

https://stackblitz.com/edit/stackblitz-starters-wnququ?file=src%2Fmain.html

但是我需要更进一步,确保索引 (x) 处的表单组的结束日期不大于或等于索引 (x + 1) 处的表单组的开始日期 - (全部在主表单内数组)

你知道我是怎么做的吗?

这就是我到目前为止所拥有的(也请参阅 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
1个回答
0
投票

首先,想澄清一下

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.