【问题标题】:Angular 4 validator to check 2 controls at the same timeAngular 4 验证器同时检查 2 个控件
【发布时间】:2018-09-28 12:22:33
【问题描述】:

我有一个带有 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”显示为无效,尽管现在它是正确的。

我了解问题是每次更改对方的值时都需要重新检查关联字段,但我不知道该怎么做。

有什么想法吗?谢谢,

【问题讨论】:

    标签: angular angular-reactive-forms angular-validation


    【解决方案1】:

    minmaxrequired 验证器可以保持原样。如果要根据另一个控件的值验证一个控件,则需要将验证提升到父控件。

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

    Live demo

    【讨论】:

    • 解决方案仍然是 +1,尽管可以将验证保持在控制级别。
    • 嗯,当然你也可以换一种方式,但我认为这更容易推理。毕竟是交叉验证,那你为什么要把这个验证保留在单独的控制级别呢?
    • 我完全同意。这就是为什么我没有说你应该,而只是说你可以
    • 啊,好吧 :) 为创造性思维投上一票^^
    • 不是很有创意,这就是他要求的(更换听众)。假设 OP 在一个问题中学习了 2 种机制!
    【解决方案2】:

    与@Tomasz 所说的不同,您可以将验证保持在控制级别。

    秘诀:监听表单的变化,当它发生时,用当前值设置验证器。

    Here is a 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;
      };
    }
    

    【讨论】:

    • (虽然我回答了你的问题,但这只是更改监听器的信息。如果你想实施一个可行的解决方案,选择@Tomasz)
    • 感谢您的帮助,虽然两个答案都有效(我做了几个测试)我决定使用您的方法,因为它更容易适应我的代码。
    • @Fel 更容易并不意味着它是最好的解决方案,甚至是一个好的实践!这听起来可能很奇怪,但您能否将另一个答案标记为有效答案,以供将来遇到与您相同问题的用户使用?
    • 没问题。我标记了你的答案,因为它是我在我的应用程序中使用的,但我可以选择另一个作为解决方案,因为我试过了,它也很好用。
    • 我遇到了一个与此相关的问题,因为我试图在两个 mat-select 中使用此解决方案。问题是两个输入都没有同时验证和更新,而是必须手动更改两个值才能使其工作。你可以在你的 stackblitz 演示中看到我的意思;如果您通过单击增加或减少值的箭头来更改值(长按 for.eg 使开始超过结束,一旦您结束长按,只检查一个验证器,而另一个错误仍然存​​在,即使它应该't)。有什么想法可以解决这个问题吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 2017-03-17
    • 1970-01-01
    相关资源
    最近更新 更多