web-dev-qa-db-fra.com

Angular 4 validateur pour vérifier 2 contrôles en même temps

J'ai une forme réactive avec 2 contrôles (port_start et port_end) qui ont les exigences suivantes:

  • Les deux doivent avoir une valeur
  • Leurs valeurs doivent être comprises entre 0 et 65535
  • la valeur port_start doit être inférieure à la valeur port_end

Voici ce que j'ai essayé jusqu'à présent:

[...]
this.formModel.addControl('port_start', 
  new FormControl(object.port_start ? object.port_start : 0, 
  [Validators.required, Validators.min(0), Validators.max(65535), this.minMaxValidator('port_start', 'port_end').bind(this)]));

this.formModel.addControl('port_end', 
  new FormControl(object.ort_end ? object.port_end : 0, 
  [Validators.required, Validators.min(0), Validators.max(65535), this.minMaxValidator('port_start', 'port_end').bind(this)]));
[...]

Voici la fonction de validation personnalisée:

minMaxValidator = function(startControl : string, endControl : string): ValidatorFn {
  return (control: FormControl): {[key: string]: any} => {
    let valid = true;
    let valStart = 0;
    let valEnd = 0;

    if(this.formModel.controls[startControl] && this.formModel.controls[endControl]) {
      valStart = <number>this.formModel.controls[startControl].value;

      valEnd = <number>this.formModel.controls[endControl].value;
    }

    valid = valEnd >= valStart;

    return valid ? null : { minmax : true };
  };
}

Cela fonctionne bien, sauf pour ce problème:

  • Disons que je tape "2" dans le champ "port_start". Angular le marque comme non valide car il est supérieur à la valeur de 'port_end' (qui est 0 par défaut). Si je tape '5' dans le champ 'port_end', l'application affiche toujours 'port_start' comme invalide, bien que maintenant il soit correct.

Je comprends que le problème est que je dois revérifier le champ associé chaque fois que je change la valeur de l'autre, mais je ne sais pas comment le faire.

Des idées? Merci,

8
Fel

Les validateurs min, max et required peuvent être conservés tels quels. Si vous souhaitez valider un contrôle en fonction de la valeur d'un autre, vous devez lever la validation vers le contrôle parent.

import { Component } from '@angular/core';
import { ValidatorFn, FormBuilder, FormGroup, Validators } from '@angular/forms';

const portStartEnd: ValidatorFn = (fg: FormGroup) => {
   const start = fg.get('portStart').value;
   const end = fg.get('portEnd').value;

   return start && end && start < end ? null : { startEnd: true };
}

@Component({
  selector: 'my-app',
  template: `
   <input [formControl]="form.get('portStart')" type="number" >
   <input [formControl]="form.get('portEnd')" type="number" >

   {{ form.valid }}
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      portStart: [null, [Validators.required, Validators.min(0), Validators.max(65535)]],
      portEnd: [null, [Validators.required, Validators.min(0), Validators.max(65535)]]
    }, { validator: portStartEnd } );
  }
}

Démo en direct

9
Tomasz Kula

Contrairement à @Tomasz, vous pouvez conserver la validation au niveau du contrôle.

Le secret: écoutez les changements sur votre formulaire, et quand cela se produit, définissez les validateurs avec les valeurs actuelles.

Voici un stackblitz qui vous montre comment cela fonctionne.

Et voici le code que j'ai utilisé:

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, ValidatorFn, AbstractControl } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `
<form [formGroup]="form">
  <input type="number" formControlName="start">
  <input type="number" formControlName="end">
</form>

<div *ngIf="form.get('start').hasError('lessThan')">Min should be less than max</div>
<div *ngIf="form.get('end').hasError('moreThan')">Max should be less than min</div>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = fb.group({
      start: [0],
      end: [65535]
    });

    this.form.valueChanges.subscribe(changes => {
      this.form.get('start').setValidators(LessThanEnd(+this.form.value.end));
      this.form.get('end').setValidators(MoreThanStart(+this.form.value.start));
    });

  }
}

export function LessThanEnd(end: number): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } => {
    return control.value > end ? { 'lessThan': true } : null;
  };
}

export function MoreThanStart(end: number): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } => {
    return control.value < end ? { 'moreThan': true } : null;
  };
}
10
user4676340