Javascript 角度5,角度材质:日期选择器验证不工作

Javascript 角度5,角度材质:日期选择器验证不工作,javascript,angular,validation,typescript,angular-material2,Javascript,Angular,Validation,Typescript,Angular Material2,我使用的是最新的角度和最新的角度材料。我有一个日期选择器,我想添加一些验证。文档说required属性应该是现成的,但它似乎不像其他表单元素那样处理错误 这是我的分数: <mat-form-field class="full-width"> <input matInput [matDatepicker]="dob" placeholder="Date of birth" [(ngModel)]="myService.request.dob" #dob="ngModel"

我使用的是最新的角度和最新的角度材料。我有一个日期选择器,我想添加一些验证。文档说
required
属性应该是现成的,但它似乎不像其他表单元素那样处理错误

这是我的分数:

<mat-form-field class="full-width">
    <input matInput [matDatepicker]="dob" placeholder="Date of birth" [(ngModel)]="myService.request.dob" #dob="ngModel" required app-validateAdult>
    <mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
    <mat-datepicker #dob></mat-datepicker>
    <mat-error *ngIf="dob.errors && dob.errors.required">Your date of birth is required</mat-error>
</mat-form-field>
*ngIf
永远不会为真,因为日期选择器似乎从不更新
dob.errors
,因此即使输入被设置为无效,也不会显示错误消息

是这样吗?我错过什么了吗

我还尝试添加一个自定义指令,以验证使用datepicker选择的日期是否表示用户已超过18岁:

export class AdultValidator implements Validator {
  constructor(
    @Attribute('app-validateAdult') public validateAdult: string
  ) { }

  validate(control: AbstractControl): { [key: string]: any } {
    const dob = control.value;
    const today = moment().startOf('day');
    const delta = today.diff(dob, 'years', false);

    if (delta <= 18) {
      return {
        validateAdult: {
          'requiredAge': '18+',
          'currentAge': delta
        }
      };
    }

    return null;
  }
}
看一看,然后。请看一个基于javascript的示例


您还可以测试此(基于角度的)日期选择器:

HTML:


您是否尝试将*ngIf设置为自定义验证器,如下所示:

 <mat-error *ngIf="dob.errors && dob.errors.validateAdult">Your date of birth 
 is less than 18 ?</mat-error>
然后使用之前的ngIf,如下所示:

 <mat-error *ngIf="dob.errors && dob.errors.requireValidator">Your date of 
birth is less than 18 ?</mat-error>
您的注册日期
出生不足18岁?
我没有测试它,但从逻辑上讲它应该可以工作。

您复制了#dob。在角度验证中可能会出现不希望出现的行为

你有

<input #dob='ngModel'

我在我的角度材质表单中使用了ErrorStateMatcher,它可以完美地工作

您应该有如下代码:

<mat-form-field class="full-width">
    <input matInput [matDatepicker]="dob" placeholder="Date of birth" formControlName="dob" required app-validateAdult>
    <mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
    <mat-datepicker #dob></mat-datepicker>
    <mat-error *ngIf="dob.hasError('required')">Your date of birth is required</mat-error>
</mat-form-field>

您可以在这里看到更多信息:

您可以更改输入引用的名称,如下所示。。 请注意,输入元素#dobInput仅在mat error中引用

 <mat-form-field class="full-width">
<input matInput [matDatepicker]="dob" placeholder="Date of birth" [(ngModel)]="myService.request.dob" #dobInput="ngModel" required app-validateAdult>
<mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
<mat-datepicker #dob></mat-datepicker>
<mat-error *ngIf="dobInput.errors && dobInput.errors.required">Your date of birth is required</mat-error>

您的出生日期是必需的

选择器由#dbo引用

[matDatepicker]=“dob”

我在没有使用
ErrorStateMatcher的情况下成功地实现了这一点,尽管这确实帮助我找到了解决方案。离开这里以备将来参考或帮助他人

我将表单转换为反应式表单,而不是模板驱动的表单,并将自定义验证器指令更改为更简单的验证器(基于非指令)

以下是工作代码:

my-form.component.html:

<div class="container" fxlayoutgap="16px" fxlayout fxlayout.xs="column" fxlayout.sm="column" *ngIf="fieldset.controls[control].type === 'datepicker'">
  <mat-form-field class="full-width" fxflex>
    <input matInput 
           [formControlName]="control"
           [matDatepicker]="dob"
           [placeholder]="fieldset.controls[control].label" 
           [max]="fieldset.controls[control].validation.max">
    <mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
    <mat-datepicker #dob></mat-datepicker>
    <mat-error *ngIf="myForm.get(control).hasError('required')">
      {{fieldset.controls[control].validationMessages.required}}</mat-error>
    <mat-error *ngIf="myForm.get(control).hasError('underEighteen')">
      {{fieldset.controls[control].validationMessages.underEighteen}}
    </mat-error>
  </mat-form-field>
</div>
my-form.component.ts:

buildForm(): void {
  const formObject = {};

  this.myService.request.fieldsets.forEach((controlsGroup, index) => {

    this.fieldsets.push({
      controlNames: Object.keys(controlsGroup.controls)
    });

    for (const control in controlsGroup.controls) {
      if (controlsGroup.controls.hasOwnProperty(control)) {
        const controlData = controlsGroup.controls[control];
        const controlAttributes = [controlData.value];
        const validators = [];

        if (controlData.validation) {
          for (const validator in controlData.validation) {
            if (controlData.validation.hasOwnProperty(validator)) {
              if (validator === 'overEighteenValidator') {
                validators.push(this.overEighteenValidator);
              } else {
                validators.push(Validators[validator]);
              }
            }
          }
          controlAttributes.push(Validators.compose(validators));
        }

        formObject[control] = controlAttributes;
      }
    }
  });

  this.myForm = this.fb.group(formObject);
}

在查看页面中添加以下内容:

      <mat-form-field>
     <input matInput [matDatepicker]="dp"   placeholder="Employement date" >
   <mat-datepicker-toggle matSuffix [for]="dp"></mat-datepicker-toggle>
   <mat-datepicker #dp></mat-datepicker>
    </mat-form-field>


只需在您的模块中导入MatDatepickerModule、MatNativeDateModule

谢谢,但所有链接都不相关。我不想使用不同的日期选择器,我不想使用Bootstrap或jQuery。我也没有使用AngularJS。问题不在于如何进行基于年龄的验证,而在于如何使用当前材料DatePickerHanks进行验证。是的,我确实将我的
mat error
上的
*ngIf
设置为
dob.errors.validateDult
-这不是问题所在。通过断点,我可以看到,在选择了小于18年的日期的情况下,自定义指令工作正常,只是出于某种原因,它没有更新
错误。我认为这是datepicker的问题,因为对于常规的
matInput
元素,内置和自定义验证按预期工作。实际上,我使用自己的指令告诉输入检查模糊验证,但如果使用angular 5,则现在内置了模糊更新事件。因此,问题可能是控件没有获得更改检测来更新其错误。请按照下面的答案进行操作,希望它能解决您的问题:)。再次感谢,我在发布问题之前确实想到了这一点,并利用
ChangeDetectorRef
触发手动更改检测,但这也不起作用。我确信这是datepicker本身的问题,也许可以尝试使用ErrorStateMatcher使错误立即出现。您可以添加详细信息和控制台日志吗?或错误屏幕。@如果没有错误屏幕或控制台错误,页面“工作”,甚至选择器附加的字段在拾取不到18年前的日期时也会显示红色错误样式,只是控件的
errors
对象没有得到更新可能是一个输入错误,但是引用是在模型引用之后留下的。。。在[(ngModel)]=“myService.request.dob#dob=”ngModel“很好地发现了@Leonardonenger!但是,唉,这只是一个打字错误,无论如何,谢谢你!谢谢,这似乎是将控件与模型中的值联系起来所必需的。如果没有这一点,错误消息仍然没有显示,而且在选择日期时,我的模型也没有更新。但是我会在没有谢谢的情况下尝试,我会尝试这个,尽管这可能需要一些时间,因为我需要切换到反应式表单而不是模板表单
<mat-datepicker #dob></mat-datepicker>
<mat-form-field class="full-width">
    <input matInput [matDatepicker]="dob" placeholder="Date of birth" formControlName="dob" required app-validateAdult>
    <mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
    <mat-datepicker #dob></mat-datepicker>
    <mat-error *ngIf="dob.hasError('required')">Your date of birth is required</mat-error>
</mat-form-field>
import { ErrorStateMatcher } from '@angular/material/core';

export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(
    control: FormControl | null,
    form: FormGroupDirective | NgForm | null
  ): boolean {
    const isSubmitted = form && form.submitted;
    return !!(
      control &&
      control.invalid &&
      (control.dirty || control.touched || isSubmitted)
    );
  }
}


export class AdultValidator implements Validator {
  dob = new FormControl('', [
    Validators.required
  ]);

  matcher = new MyErrorStateMatcher();
}
 <mat-form-field class="full-width">
<input matInput [matDatepicker]="dob" placeholder="Date of birth" [(ngModel)]="myService.request.dob" #dobInput="ngModel" required app-validateAdult>
<mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
<mat-datepicker #dob></mat-datepicker>
<mat-error *ngIf="dobInput.errors && dobInput.errors.required">Your date of birth is required</mat-error>
[matDatepicker]="dob"

<mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
<div class="container" fxlayoutgap="16px" fxlayout fxlayout.xs="column" fxlayout.sm="column" *ngIf="fieldset.controls[control].type === 'datepicker'">
  <mat-form-field class="full-width" fxflex>
    <input matInput 
           [formControlName]="control"
           [matDatepicker]="dob"
           [placeholder]="fieldset.controls[control].label" 
           [max]="fieldset.controls[control].validation.max">
    <mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
    <mat-datepicker #dob></mat-datepicker>
    <mat-error *ngIf="myForm.get(control).hasError('required')">
      {{fieldset.controls[control].validationMessages.required}}</mat-error>
    <mat-error *ngIf="myForm.get(control).hasError('underEighteen')">
      {{fieldset.controls[control].validationMessages.underEighteen}}
    </mat-error>
  </mat-form-field>
</div>
import { ValidatorFn, AbstractControl } from '@angular/forms';
import * as moment from 'moment';

export function overEighteen(): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } => {
    const dob = control.value;
    const today = moment().startOf('day');
    const delta = today.diff(dob, 'years', false);

    if (delta <= 18) {
      return {
        underEighteen: {
          'requiredAge': '18+',
          'currentAge': delta
        }
      };
    }

    return null;
  };
}
buildForm(): void {
  const formObject = {};

  this.myService.request.fieldsets.forEach((controlsGroup, index) => {

    this.fieldsets.push({
      controlNames: Object.keys(controlsGroup.controls)
    });

    for (const control in controlsGroup.controls) {
      if (controlsGroup.controls.hasOwnProperty(control)) {
        const controlData = controlsGroup.controls[control];
        const controlAttributes = [controlData.value];
        const validators = [];

        if (controlData.validation) {
          for (const validator in controlData.validation) {
            if (controlData.validation.hasOwnProperty(validator)) {
              if (validator === 'overEighteenValidator') {
                validators.push(this.overEighteenValidator);
              } else {
                validators.push(Validators[validator]);
              }
            }
          }
          controlAttributes.push(Validators.compose(validators));
        }

        formObject[control] = controlAttributes;
      }
    }
  });

  this.myForm = this.fb.group(formObject);
}
      <mat-form-field>
     <input matInput [matDatepicker]="dp"   placeholder="Employement date" >
   <mat-datepicker-toggle matSuffix [for]="dp"></mat-datepicker-toggle>
   <mat-datepicker #dp></mat-datepicker>
    </mat-form-field>