如何同时进行formControl验证

时间:2016-12-21 21:58:26

标签: angular angular2-template angular2-forms

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

我还有2个自定义验证程序指令,我在这两个表单控件上使用。一个是检查给定的输入日期是否小于对照的日期。另一个检查给定的输入日期是否大于对照的日期。

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

  1. startDate和endDate必须在将来(比现在更晚)
  2. startDate必须在endDate之前
  3. 所以,我的问题是,当我更新其中一个日期时,验证只发生在我更新的日期。例如:

    1. 我首先将startDate设置为有效的未来日期。
    2. 我将startDate设置为startDate之前。这使得endDate无效。
    3. 我将startDate更改为endDate之前的有效未来日期。 我希望endDate变为有效,即使它更改了startDate。
    4. 我的问题是:我如何优雅地在另一个控件上运行有效性?

      父组件的模板:

      <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>
      

      日期少,也就是说-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;
          };
        }
      }
      

      日期大于-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 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;
          };
        }
      }
      

2 个答案:

答案 0 :(得分:0)

您正在观察的行为是非常期待的。仅针对由用户交互更改的控件触发验证。

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

  • startDate|endDate创建自定义验证程序。它只会验证当前是否有formControl值。
  • 为包裹formGroup的{​​{1}}创建自定义验证程序。这样,当任何字段被更改时,您将能够检查是否开始&lt;结束日期。

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

我希望这对你有用。

答案 1 :(得分:0)

我的方法奏效了

  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]);