如何直接从 formControl 获取验证器?

How can I get Validators from the formControl directly?

我有一个电子邮件字段,我添加了两个验证器(必填,电子邮件),我想在 input event 上检查验证状态以调用 API 检查我系统的成员时它是有效的,如果它是 invalid 不要调用 API 并且不要在页面上显示错误消息。

我将在模糊事件(focusOut 电子邮件的输入)上显示错误消息,首先我使用 formControl.validator(formControl) 触发验证器并检查 formControl.valid,我获得了有效状态成功,但它会显示错误消息该页面是因为我订阅了 statsuChange 以在状态等于 invalid.

时显示错误消息

目前,我将验证器保存在一个变量中并传递给 initEmailChaingeEvent() 以在没有 statusChange 事件的情况下检查验证状态。 它可以工作,但我认为这不是一个好方法,这是我的实现示例:

live example

export class AppComponent {
  public sampleForm: FormGroup;

  @ViewChild('emailElm')
  emailElm: ElementRef;

  ngOnInit() {
    this.initForm();
  }

  initForm() {
    const emailValidtors = [
      Validators.required,
      Validators.email
    ]

    const emailFC = new FormControl(null, {
      validators: emailValidtors,
      updateOn: 'blur'
    });
    //
    this.sampleForm = new FormGroup({
      'email': emailFC
    });
    //
    this.initEmailChaingeEvent({
      emailFC: emailFC,
      validtors: emailValidtors
    });
    //
    this.sampleForm.valueChanges.subscribe((val) => {
      console.log('_blue event:valueChanges', val)
    });
    //
    this.initShowErrorMsgEvent({
      fc: emailFC
    });
  }

  private initEmailChaingeEvent(arg: {
    emailFC: FormControl,
    validtors: any[]
  }) {
    fromEvent(this.emailElm.nativeElement, 'input')
      .pipe(debounceTime(500))
      .subscribe((event: Event) => {
        const currentEmail = (event.target as HTMLInputElement).value;
        // check is valid
        // **quention** : how can I get validtors from fromcontrol self 
        // if I use  arg.emailFC.validator(arg.emailFC) to ehcek status , it will trigger oberservable in initShowErrorMsgEvent(),
        //  but I just want to got valid result , I don't want to show error msg on UI at this time

        for (const validator of arg.validtors) {
          const inValid = validator(arg.emailFC);
          if (inValid) {
            console.log('input event:invalid', currentEmail);
            return;
          }
        }
        // **do sometheng when all valid**
        console.log('input event:call API , email:',currentEmail );
      });
  }

  private initShowErrorMsgEvent(arg: {
    fc: FormControl,
  }) {
    arg.fc.statusChanges
      .subscribe((status) => {
        // console.log('status' , status);
        if (status === 'INVALID') {
          // show error msg....
          console.log('_show error msg by antoher component');
        }
      });
  }
}
<form [formGroup]="sampleForm">
  <input formControlName="email" #emailElm >
</form>

这里有很多事情需要做一些工作。首先,我会订阅控件的 formValue 更改,而不是在本机元素上使用 Observable。

this.sampleForm.get('email').valueChanges.subscribe((event) => {
    // CODE IN HERE
}

ValueChanges 是 formControl 上的一个 属性,它会在值更改时发出事件。 https://angular.io/api/forms/AbstractControl

接下来,要访问验证器,您可以简单地检查表单控件是否有效。

this.sampleForm.get('email').valid();

如果任何验证器无效,则为假,如果所有验证器均有效,则为真。

编辑:

根据你的评论,听起来你想要一个 debounceTime 管道:

this.sampleForm.get('email').valueChanges
.pipe(debounceTime(500), distinctUntilChanged())
.subscribe((event) => {
    if (this.sampleForm.get('email').valid) {
         // Your API logic here
    }
}

DebounceTime 基本上会延迟事件的处理,并会抛出队列中任何待处理的事件。

debounceTime delays values emitted by the source Observable, but drops previous pending delayed emissions if a new value arrives on the source Observable. http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-debounceTime

你把任务复杂化了。 首先,声明您的表单并控制验证器。如果您从验证器列表中组成一个验证器,它们将按定义顺序进行检查。因此,首先是必需的,然后是 "if it's valid email",最后您的自定义验证器将检查它是否唯一

 ....
 this.emailForm = this.fb.group({
       emailAddress: [null, Validators.compose([Validators.required, Validators.email, uniqueEmailValidator()])]
 });
 ...

然后您创建自定义验证器以通过 API 检查用户电子邮件。有关自定义验证器的更多信息:Angular docs

function uniqueEmailValidator(){
    ...
}

谢谢大家多多指点,我通过formControl's validator属性修正了它,这是我修改后的列表:

  • 仅将值传递给验证器避免触发 statusChange 发射器

live example

private initEmailChaingeEvent(arg:{
   emailFC : FormControl,
   ....
}) {
  ...
  const inValid = arg.emailFC.validator({
      value: currentEmail
    }
    as any);
  ....
}