web-dev-qa-db-ja.com

Angular 2つのコントロールを同時にチェックする4つのバリデーター

次の要件を持つ2つのコントロール(port_startとport_end)を備えたリアクティブフォームがあります。

  • 両方に値が必要です
  • それらの値は0から65535の間でなければなりません
  • port_start値は、port_end値よりも小さい必要があります

これは私がこれまでに試したことです:

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

これはカスタム検証関数です:

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

これはこの問題を除いて正常に動作します:

  • 「port_start」フィールドに「2」と入力したとします。 Angularは、「port_end」の値(デフォルトでは0)より大きいため、無効としてマークします。「port_end」フィールドに「5」と入力しても、アプリは引き続き表示されます'port_start'は無効ですが、現在は正しくなっています。

問題は、他のフィールドの値を変更するたびに関連フィールドを再確認する必要があることですが、その方法がわかりません。

何か案は?おかげで、

8
Fel

minmaxおよびrequiredバリデーターはそのまま保持できます。あるコントロールを別のコントロールの値に基づいて検証する場合は、検証を親コントロールに上げる必要があります。

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

ライブデモ

9
Tomasz Kula

@Tomaszが言ったのとは異なり、検証をコントロールレベルで維持できます。

秘密:フォームの変更をリッスンし、それが発生したときに、バリデーターに現在の値を設定します。

これはstackblitzですそれがどのように機能するかを示します。

そしてここに私が使用したコードがあります:

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