Angular 2 个反应式表单自定义验证器仅在表单控件有效时应用

Angular 2 reactive forms custom validator apply only when form control is valid

如何实现仅在表单控件有效时应用的自定义验证器?

像这样的东西是理想的:

static isValid(control: FormControl) {
    if (control.valid) {
        // custom validation checks here
        return {isNotValid: true};
    }
    return null;
}

但这里 control.valid 始终为真,因此即使其他人会使该字段无效,它也会被应用。

有办法实现吗?

详细示例

源代码在这里:https://stackblitz.com/edit/angular-conditional-validator

app.component.ts

import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { MyValidator } from './validators.service';

@Component({
  selector: 'my-app',
  template: `
    <form>
      <label>Name:</label>
      <input [formControl]="form.get('name')">
    </form>
  `
})
export class AppComponent  {
  form = new FormGroup ({
    name: new FormControl('', [
      MyValidator.isValidString,
      MyValidator.isValidName,
    ])
  });
}

validators.service.ts

import { FormControl } from '@angular/forms';

export class MyValidator {
  static isValidString(control: FormControl) {
    if (!control.value || typeof control.value !== 'string') {
      return {isNotValidString: true};
    }
    return null;
  }

  static isValidName(control: FormControl) {
    if (control.valid && control.value !== 'John Doe') {
      return {isNotValidName: true};
    }
    return null;
  }
}

如何使 isValidName 验证器仅在控制有效时才 applied/executed,即先前的验证器返回 null? 因为现在,我相信 angular 将首先 运行 所有同步验证器,然后 运行 所有异步验证器,然后才设置控制状态,这是我认为的正确方法。

注意
本例仅供演示,没有实际应用。

尝试访问父级 属性,假设 isValid 验证器用于表单控件的第一级子级控件,否则递归地找到顶部的父级

static isValid(control: FormControl) {
    if(control.parent) {
       if(control.parent.valid) {
       //custom validation
       }
    }
 return null;
}

实现此目的的最简单方法是创建辅助函数:
validators.service.ts

import { AbstractControl, FormControl, ValidationErrors, ValidatorFn } from "@angular/forms";

export function runInOrder(validators: ValidatorFn[]): ValidatorFn {
  return (c: AbstractControl): ValidationErrors | null => {
    for (const validator of validators) {
      const resp = validator(c);
      if (resp != null) {
        return resp;
      }
    }
    return null;
  };
}

export class MyValidator {
  // ...
}

并将其用作自定义验证器:
app.component.ts

import { MyValidator, runInOrder } from "./validators.service";

// ...
export class AppComponent {
  form: FormGroup = new FormGroup({
    name: new FormControl("", runInOrder([
      MyValidator.isValidString,
      MyValidator.isValidName
    ]))
  });
}

完整示例在这里:
https://stackblitz.com/edit/angular-conditional-validator-solution