首页 > 解决方案 > 当具有多个 AsyncValidator 时,Angular FormBuilder 处于待处理状态

问题描述

我正在尝试在 Angular 中向我的表单添加一些自定义验证。问题是当我在表单中只有一个 AsyncValidator 时,一切都按预期工作,所以我更改了一个值并且表单状态更改为Valid. 当我有两个 AsyncValidator 时,表单会保持Pending状态,直到我更改它们。

这是我的代码:

uniqueValue.validator.ts

export function UniqueValidator(service: ExistsService, originalValue: string = null): AsyncValidatorFn {
    return control => {
        if (control.valueChanges == null) {
            return of(null);
        }

        return control.valueChanges
            .pipe(
                debounceTime(400),
                distinctUntilChanged(),
                switchMap(value => {
                    if (value === originalValue) {
                        control.markAsPristine();
                        return of(false);
                    }

                    return service.exists(value);
                }),
                map((unique: boolean) => (!unique ? null : { 'notUnique': true })),
                first());
    }
}

一些组件.component.ts

ngOnInit() {
  this.form= this.fb.group({
    email: [this.email,
      [Validators.required],
      UniqueValidator(this.myService)
    ],
    username: [this.userName,
      [Validators.required],
      UniqueValidator(this.myService)
    ],
    fullName: [this.fullName]
  });

  this.form.markAsPristine();
}

submitButtonEnabled(): boolean {
  return this.form.valid
    && this.form.dirty;
}

ps 这是一个Edit Form,因此所有字段的初始状态中总会有值。因此,当表单首次加载时,两个字段(电子邮件和用户名)都是有效的,并且其中一个更改将触发 AsyncValidator,如果响应为 true,submitButtonEnabled则应返回 true。

标签: angularangular-forms

解决方案


AsyncValidator 比您的代码更容易一些。(真的你不能使用 valueChanges observable)。

想象一下,你有一个 dataService 有两个函数,如果存在则返回一个可观察的 true,如果不存在则返回 false。好吧,我通过使用“of”rxjs 运算符和延迟来模拟

export class DataService {

  constructor() { }
  emails=['qqq@qqq.com','aaa@aaa.com']
  userNames=['qqq','aaa','bbb']
  existEmail(email:string)
  {
   return of(this.emails.indexOf(email)>=0?true:false).pipe(delay (1000))
  }
  existUser(userName:string)
  {
   return of(this.userNames.indexOf(userName)>=0?true:false).pipe(delay (1000))
  }
}

您可以创建一个像

form=new FormGroup( {
    name:new FormControl('',{asyncValidators:this.uniqueName(),updateOn:'blur'}),
    email:new FormControl('',{asyncValidators:this.uniqueEmail(),updateOn:'blur'}),
  })
  uniqueName()
  {
    return (control:FormControl)=>{
       return this.dataService.existUser(control.value).pipe(map(x=>
       {
         return x?{error:'yet exist'}:null
       }))
    }
  }
  uniqueEmail()
  {
    return (control:FormControl)=>{
       return this.dataService.existEmail(control.value).pipe(map(x=>
       {
         return x?{error:'yet exist'}:null
       }))
    }
  }

查看堆栈闪电战

考虑到:

  1. 我使用 {updateOn:'blur'} 只检查何时模糊
  2. 验证器返回一个 Observable,如果错误,则返回对象 {error:'yet exists'} 的可观察对象,如果有效,则返回 null 的可观察对象。
  3. 从 dataService 你收到真 - 如果存在 - 或假 - 如果不 - 我使用 (pipe(map) 来转换响应,并使用 'of' rxjs 返回可观察的

如果你想使用延迟,因为你不喜欢 {updateOn:'blur'} 我建议使用 tro auxiliars formControls

control1=new FormControl()
control2=new formControl()
form=new FormGroup( {
        name:new FormControl('',{asyncValidators:this.uniqueName()}),
        email:new FormControl('',{asyncValidators:this.uniqueEmail()}),
      })

this.control1.valueChanges.pipe(
      debounceTime(400),
      distinctUntilChanged()).subscribe(res=>{
          this.form.get('name').setValue(res)
      })

this.control2.valueChanges.pipe(
      debounceTime(400),
      distinctUntilChanged()).subscribe(res=>{
          this.form.get('email').setValue(res)
      })

你不使用表格,你使用

<input [formControl]="control1">
<input [formControl]="control2">

推荐阅读