【问题标题】:How to do simultaneous formControl validation如何同时进行formControl验证
【发布时间】:2017-05-07 10:57:38
【问题描述】:

我有一个包含 2 个日期时间选择器组件的表单的父组件,它们是 formControls。这些代表 startDate 和 endDate。

我还有 2 个自定义验证器指令用于这 2 个表单控件。一是检查给定的输入日期是否小于控件的日期。另一个检查给定的输入日期是否大于控件的日期。

这 2 个验证器允许我验证 startDate 和 endDate 的以下条件:

  1. startDate 和 endDate 必须在将来(比现在晚)
  2. startDate 必须在 endDate 之前

所以,我的问题是,当我更新其中一个日期时,验证只会发生在我更新的日期。例如:

  1. 我将 startDate first 设置为一个有效的未来日期。
  2. 我将 endDate 设置在 startDate 之前。这会使 endDate 无效。
  3. 我将 startDate 更改为 endDate 之前的有效未来日期。 我希望 endDate 生效,即使 startDate 发生了变化。

我的问题是:如何优雅地使有效性在另一个控件上运行?

父组件模板:

<form #projectForm="ngForm" novalidate class="row">

    <div class="form-group col-md-6">
      <label for="startDate">Start Date</label>
      <date-time-picker #startDate="ngModel" name="startDate" [(ngModel)]="project.startDate" [disabled]="isReadOnly" [dateLessThan]="project.endDate" [dateGreaterThan]="now"></date-time-picker>
      <div *ngIf="startDate.errors">
        <div [hidden]="startDate.valid" *ngIf="startDate.errors.dateLessThan" class="alert alert-danger">Start date should be before end date</div>
        <div [hidden]="startDate.valid" *ngIf="startDate.errors.dateGreaterThan" class="alert alert-danger">Date should be in the future</div>
      </div>
    </div>

    <div class="form-group col-md-6">
      <label for="endDate">End Date</label>
      <date-time-picker #endDate="ngModel" name="endDate" [(ngModel)]="project.endDate" [disabled]="isReadOnly" [dateGreaterThan]="maxDate()"></date-time-picker>
      <div *ngIf="endDate.errors">
        <div [hidden]="endDate.valid" *ngIf="endDate.errors.dateGreaterThan" class="alert alert-danger">End date is too early</div>
      </div>
    </div>
  </div>
</div>

date-less-that-validator.directive.ts:

import { Directive, forwardRef, Input } from '@angular/core';
import { NG_VALIDATORS, FormControl } from '@angular/forms';
import * as moment from 'moment';

/**
 * Directive to validate whether FormControl's date is less than input date
 */
@Directive({
  selector: '[dateLessThan][ngModel],[dateLessThan][formControl]',
  providers: [
    { provide: NG_VALIDATORS, useExisting: forwardRef(() => DateLessThanValidatorDirective), multi: true }
  ]
})
export class DateLessThanValidatorDirective {

  validator: Function;

  constructor() { //If validating onEndDate, reverse the otherDate
      this.validator = this.dateLessThan();
  }

  @Input('dateLessThan') inputDate: Date; // Date comparing against.

  validate(c: FormControl) {
    return this.validator(c);
  }

  /**
 * Factory method that creates a function that accepts a form control.
 * Returns null if form is valid. Returns an object that contains error message if invalid.
 */
  dateLessThan() {
    return (c: FormControl) => {

      let controlDate = c.value;

      if (controlDate && this.inputDate) { //Only if both dates are set do we do validation
        if (moment(controlDate).diff(this.inputDate) > 0) {
          return {
            dateLessThan: 'Controls date is greater than given date'
          };
        }
      }
      return null;
    };
  }
}

日期大于验证器.directive.ts:

import { Directive, forwardRef, Input } from '@angular/core';
import { NG_VALIDATORS, FormControl } from '@angular/forms';
import * as moment from 'moment';

/**
 * Directive to validate whether FormControl's date is greater than input date
 */
@Directive({
  selector: '[dateGreaterThan][ngModel],[dateGreaterThan][formControl]',
  providers: [
    { provide: NG_VALIDATORS, useExisting: forwardRef(() => DateGreaterThanValidatorDirective), multi: true }
  ]
})
export class DateGreaterThanValidatorDirective {

  validator: Function;

  constructor() { //If validating onEndDate, reverse the otherDate
      this.validator = this.dateGreaterThan();
  }

  @Input('dateGreaterThan') inputDate: Date; // Date comparing against.

  validate(c: FormControl) {
    return this.validator(c);
  }

  /**
 * Factory method that creates a function that accepts a form control.
 * Returns null if form is valid. Returns an object that contains error message if invalid.
 */
  dateGreaterThan() {
    return (c: FormControl) => {

      let controlDate = c.value;

      if (controlDate && this.inputDate) { //Only if both dates are set do we do validation
        if (moment(controlDate).diff(this.inputDate) < 0) {
          return {
            dateGreaterThan: 'Controls date is less than given date'
          };
        }
      }

      return null;
    };
  }
}

【问题讨论】:

    标签: angular angular2-template angular2-forms


    【解决方案1】:

    我的方法奏效了:

      private MaxCompareValidator(maxformControl: FormControl): ValidatorFn {
        let subscribe = false;
        return (control: AbstractControl): { [key: string]: boolean } | null => {
            if (!subscribe) {
    
                subscribe = true;
                maxformControl.valueChanges.subscribe(() => {
                    control.updateValueAndValidity();
                });
            }
    
            if (!maxformControl || !maxformControl.value)
                return { dateCompareInvalid: false };
            if (control.value && new Date(control.value) > new Date(maxformControl.value)) {
                return { dateCompareInvalid: true };
            }
            return { dateCompareInvalid: false };
        };
    }
    
     var newServiceStartTimeCtrl = new FormControl();
                                newServiceStartTimeCtrl.setValue(activity.startDate.toISOString());
    
                                var newServiceEndTimeCtrl = new FormControl();
                                newServiceEndTimeCtrl.setValue(activity.endDate.toISOString());
    
                                newServiceStartTimeCtrl.setValidators([Validators.required, this.MaxCompareValidator(newServiceEndTimeCtrl)]);
                                newServiceEndTimeCtrl.setValidators([Validators.required]);
    

    【讨论】:

      【解决方案2】:

      您所观察到的行为是完全可以预料的。验证仅针对由用户交互更改的控件触发。

      您可以通过以下方式处理该场景:

      • startDate|endDate 创建自定义验证器。它只会验证当前的formControl 值是否在未来。
      • 为包装startDate|endDateformGroup 创建自定义验证器。这样,当任何字段发生更改时,您将能够检查是否开始

      实现两个验证器很好地分离了职责并将域逻辑封装在每个验证器中。

      我希望这对你有意义。

      【讨论】:

      • 这是有道理的,但是当我将控件之外的变量传递给验证器时,不应该用角度来查找这些变量的更新/更改吗?
      • 我不这么认为。验证器一次仅与一个日期时间选择器相关联。如果您通过使用ngModelGroup 为组应用两个验证器,则在更改任一字段时都将运行。
      猜你喜欢
      • 1970-01-01
      • 2017-12-06
      • 1970-01-01
      • 2021-11-06
      • 2021-01-07
      • 1970-01-01
      • 1970-01-01
      • 2016-02-25
      • 2017-11-11
      相关资源
      最近更新 更多