Angular 依赖于其他表单控件的表单控件验证器

Angular formControl validators that depend on other formcontrols

我需要创建一个包含 2 个日期的表单,dateFrom 和 dateTo。 验证条件是dateFrom不能在dateTo之后,dateTo不能在dateFrom之前。

所以我创建了一个表单组,其中包含两个表单控件和一个共同的验证器,用于检查此条件。

export class DateRangeSelector {

    dateForm: FormGroup = new FormGroup({
         dateFrom: new FormControl({ year: 2017, month: 10 },[this.dateValidator.bind(this)]),
         dateTo: new FormControl({ year: 2020, month: 11 }, [this.dateValidator.bind(this)])
    });

dateValidator(control: FormControl): { [s: string]: boolean } {
    const valueDateFrom = this.dateForm.get('dateForm').value;
    const valueDateTo = this.dateForm.get('dateTo').value;
    if (valueDateFrom && valueDateTo) {
        //please ignore the fact that value is {year: x, month: y}, I need  to parse
        const dateFrom = moment(valueDateFrom);
        const dateTo = moment(valueDateTo);
        if (dateFrom.isAfter(dateTo)) {
            return { invalidDate: true };
        }
    }
    return null;
    }
}

我的问题是当验证器尝试验证时,this.dateForm 未定义(不在上下文中)。我不明白,因为我 绑定了 验证器声明 中的方法。

您可以将控件定义为

new FormGroup({
        dateFrom: new FormControl('', [validateStartResult]),
        dateTo: new FormControl('', [validateEndResult]),
})

然后创建单独的文件进行验证

import {AbstractControl, ValidationErrors, ValidatorFn} from "@angular/forms";

enum ValidationFor {
    end = 'end',
    start = 'start'
}

export const validateEndResult: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
    return validateResult(control, ValidationFor.end);
}
export const validateStartResult: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
    return validateResult(control, ValidationFor.start);
}

function validateResult(control, currentValidation: ValidationFor): ValidationErrors | null {
    const startControl = control?.root?.get('dateFrom');
    const endControl = control?.root?.get('dateTo');

    const currentTime = new Date().getTime();
    const startTimeStamp = new Date(startControl?.value).getTime(); // configurations yours date
    const endTimeStamp = new Date(endControl?.value).getTime(); // configurations yours date

    const error: { [key: string]: boolean } = {};
    let hasError = false;

    if (startTimeStamp > endTimeStamp) { // main codition
        if (currentValidation === ValidationFor.end) {
            error.endResult = true
        }
        if (currentValidation === ValidationFor.start) {
            error.startResult = true
        }
        hasError = true;
    }

    return hasError ? error : null;
}

您可以按照本指南完成自己的验证

检查 formGroup 验证器后:

日期表单:表单组;

dateForm: FormGroup;

constructor(formBuilder: FormBuilder, private toastService: ToastService) {
    this.dateForm = formBuilder.group({
        dateFrom: { year: 2017, month: 10 },
        dateTo: { year: 2018, month: 11 }
    });
    this.dateForm.setValidators(this.dateValidator());
}

dateValidator(): ValidatorFn {
    return (group: FormGroup): ValidationErrors => {
        const controlDateFrom = group.controls.dateFrom;
        const controlDateTo = group.controls.dateTo;
        if (controlDateFrom.value && controlDateTo.value) {
            const dateFrom = moment(controlDateFrom.value);
            const dateTo = moment(controlDateTo.value);
            if (dateFrom.isAfter(dateTo)) {
                controlDateFrom.setErrors({ invalidDateRange: true});
                controlDateFrom.setErrors({ invalidDateRange: true});
            }else{
                controlDateFrom.setErrors(null);
                controlDateTo.setErrors(null);
            }
        }
        return;
    };
}

这个解决方案特别有趣,因为它绑定到组件,例如,当日期错误时,我可以举个例子。或者我可以在设置日期值之前对其进行格式化,这样可以避免所有错误。

我认为 this.dateForm 未定义的原因是因为 Validatdor 在创建单个表单控件本身时被执行,即当 属性 dateForm 仍在创建时。

您可以在现有代码的验证器中执行此操作来验证这一点。

  dateValidator() {
    console.log(this.dateForm);
  }

您会看到该组在两个 undefined

之后打印出来

处理此问题的一个好方法是使用组验证器,例如:

dateForm: FormGroup = new FormGroup(
  {
    dateFrom: new FormControl({ year: 2017, month: 10 }),
    dateTo: new FormControl({ year: 2020, month: 11 }),
  },
  this.dateValidator.bind(this)
);

dateValidator(group: FormGroup) {
  const valueDateFrom = group.get('dateFrom').value;
  const valueDateTo = group.get('dateTo').value;
  if (valueDateFrom && valueDateTo) {
    //please ignore the fact that value is {year: x, month: y}, I need  to parse
    const dateFrom = new Date(valueDateFrom).getTime(); // moment(valueDateFrom)
    const dateTo = new Date(valueDateTo).getTime(); // moment(valueDateTo);
    if (dateFrom > dateTo) {
      console.log('Error =====> ');
      return { invalidDate: true };
    }
  }

  console.log('Success =====> ');
  return null;
}

Example