webentwicklung-frage-antwort-db.com.de

Angular 5, Angular Material: Datepicker-Validierung funktioniert nicht

Ich verwende das neueste Winkelstück und das neueste Winkelmaterial. Ich habe einen Datepicker und möchte etwas Validierung hinzufügen. In Dokumenten heißt es, dass das required-Attribut sofort funktionieren sollte, es scheint jedoch, dass Fehler nicht auf die gleiche Weise behandelt werden wie andere Formularelemente.

Hier ist mein Aufschlag:

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

Dies funktioniert auf dem Happy-Pfad. Wenn ein Datum ausgewählt wird, endet das Datum in der erwarteten Eigenschaft in myService.

Die Validierung funktioniert jedoch nicht so, wie ich es mir vorgestellt hätte. In diesem Fall klicke ich in das Feld und dann aus dem Feld heraus, ohne ein Datum einzugeben. Die Eingabe do erhält rotes Styling, aber das übliche [controlName].errors-Objekt wird nicht gefüllt. Das bedeutet, dass das Anzeigen einer Fehlermeldung auf die übliche Weise (die mit anderen Eingaben arbeitet, die keine Datumsauswahl auf derselben Seite sind) nicht funktioniert:

<mat-error *ngIf="dob.errors && dob.errors.required">Your date of birth is required</mat-error>

Der *ngIf ist nie wahr, da der Datepicker scheinbar nie dob.errors zu aktualisieren scheint. Daher wird die Fehlernachricht nie angezeigt, auch wenn die Eingabe als ungültig markiert wurde.

Ist das richtig? Habe ich etwas verpasst?

Ich habe auch versucht, eine benutzerdefinierte Direktive hinzuzufügen, um zu bestätigen, dass das mit dem Datepicker ausgewählte Datum angibt, dass der Benutzer über 18 Jahre alt ist:

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

In diesem Fall versuche ich, eine ähnliche matError (mit Ausnahme von dob.errors.validateAdult) zu verwenden, um den Fehler ggf. anzuzeigen.

Das Interessante dabei ist, dass, wenn ich ein Datum vor weniger als 18 Jahren auswähle, die gesamte Eingabe, das Etikett usw. das standardmäßige rote Fehler-Styling erhält, also etwas passiert, aber meine Fehlermeldung immer noch nicht angezeigt wird.

Anregungen wären sehr dankbar!

Genaue Versionen:

Angular CLI: 1.6.3
Node: 6.11.0
OS: win32 x64
Angular: 5.1.3
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

@angular/cdk: 5.0.4
@angular/cli: 1.6.3
@angular/flex-layout: 2.0.0-beta.12
@angular/material-moment-adapter: 5.0.4
@angular/material: 5.0.4
@angular-devkit/build-optimizer: 0.0.36
@angular-devkit/core: 0.0.22
@angular-devkit/schematics: 0.0.42
@ngtools/json-schema: 1.1.0
@ngtools/webpack: 1.9.3
@schematics/angular: 0.1.11
@schematics/schematics: 0.0.11
TypeScript: 2.4.2
webpack: 3.10.0
9
danwellman

Ich habe es geschafft, dies ohne die ErrorStateMatcher zum Laufen zu bringen, obwohl ich dadurch die Lösung gefunden habe. Verlassen hier für zukünftige Referenz oder um anderen zu helfen.

Ich habe mein Formular in ein reaktives Formular anstelle eines vorlagenbasierten Formulars konvertiert und die benutzerdefinierte Validator-Direktive in einen einfacheren (nicht auf Direktiven basierenden) Validierer geändert.

Hier ist der Arbeitscode:

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

note : Der obige Code befindet sich in ein paar verschachtelten ngFor-Schleifen, die den Wert von fieldset und control definieren. In diesem Beispiel ist control der Zeichenfolge dob zugeordnet.

über-achtzehn.validator.ts:

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

mein-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);
}
2
danwellman

Ich verwende ErrorStateMatcher in meinen Angular Material Forms, es funktioniert perfekt.

Sie sollten einen Code haben, der so aussieht:

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

Und TypeScript:

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();
}

Sie können hier mehr darüber erfahren: https://material.angular.io/components/input/overview

4
Manon Ingrassia

Fügen Sie dies auf Ihrer Ansichtsseite hinzu: 

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

Importieren Sie einfach MatDatepickerModule, MatNativeDateModule in Ihr Modul

1
Ajai

Sie können den Namen der Eingabereferenz wie folgt ändern .. Beachten Sie, dass das Eingabeelement #dobInput nur in mat-error referenziert wird. 

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

Der Picker wird von #dbo referenziert 

[matDatepicker]="dob"

<mat-datepicker-toggle matSuffix [for]="dob"></mat-datepicker-toggle>
1

Sie haben den #dob dupliziert. Dies kann bei der Winkelprüfung ein unerwünschtes Verhalten haben.

Du hast 

<input #dob='ngModel'

und 

<mat-datepicker #dob></mat-datepicker>

Bitte korrigieren Sie die Namenskonvention und sehen Sie, was passiert.

1

Haben Sie versucht, die * ngIf wie folgt auf Ihren benutzerdefinierten Prüfer zu setzen:

 <mat-error *ngIf="dob.errors && dob.errors.validateAdult">Your date of birth 
 is less than 18 ?</mat-error>

Wenn dies funktioniert, können Sie einen anderen Prüfer erstellen, um das erforderliche Verhalten der systemeigenen Prüfung zu simulieren.

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

  validate(control: AbstractControl): { [key: string]: any } {
    let value = control.value;
    if (!value || value == null || value.toString().length == 0) {
        return requireValidator: {
      'requiredAge': 'This field is required',
    }
    }

    return null;
  }
}

Und dann verwenden Sie den vorherigen ngIf wie folgt:

 <mat-error *ngIf="dob.errors && dob.errors.requireValidator">Your date of 
birth is less than 18 ?</mat-error>

Ich habe es nicht getestet, aber logischerweise sollte es funktionieren.

0
Nour