【问题标题】:Angular FormBuilder in Pending state when having more than one AsyncValidator当具有多个 AsyncValidator 时,Angular FormBuilder 处于待处理状态
【发布时间】:2020-05-25 13:11:48
【问题描述】:

我正在尝试在 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());
    }
}

some-component.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;
}

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

【问题讨论】:

    标签: angular angular-forms


    【解决方案1】:

    试试这个:

      if (!control.valueChanges) {
        return of(null as ValidationErrors);
      }
    
      if (!obs) {
        obs =  merge(
          of(control.value),
          control.valueChanges
        ).pipe(
          debounceTime(500),
          distinctUntilChanged(),
          switchMap((login: string) => {
            ...
          }),
          map(_ => {
            return id == null ?
              null :
              { exists: true };
          })
        );
      }
    
      return obs.pipe(first());
    

    【讨论】:

      【解决方案2】:

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

      stackblitz

      考虑:

      1. 我使用 {updateOn:'blur'} 只检查何时模糊
      2. 验证器返回一个 Observable,以防出错 对象 {error:'yet exist'} 的可观察值,如果有效 可观察到的 null。
      3. 从 dataService 收到 true - 如果存在 - 或 false - 如果不存在 - 我使用 (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">
      

      【讨论】:

      • 看来valueChanges 是造成这种情况的原因。我所做的只是将if(value === originalValue) 放在管道外(在UniqueValidatorByFunction 级别)并且它工作得很好。
      猜你喜欢
      • 2018-11-19
      • 2014-06-21
      • 2018-03-31
      • 1970-01-01
      • 1970-01-01
      • 2021-10-12
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多