Angular FormBuilder в состоянии ожидания, когда имеется более одного AsyncValidator - PullRequest
0 голосов
/ 10 февраля 2020

Я пытаюсь добавить пользовательскую проверку в мою форму в Angular. Проблема в том, что когда у меня есть только один AsyncValidator в форме, все работает как положено, поэтому я меняю одно значение и статус формы меняется на Valid. Когда у меня есть два AsyncValidators, форма остается в состоянии 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;
}

ps это Edit Form, поэтому всегда будут значения в начальном состоянии для всех полей. Таким образом, когда форма впервые загружена, оба поля (адрес электронной почты и имя пользователя) являются действительными, и одно из них изменяется, это вызовет AsyncValidator, и если ответ верен, submitButtonEnabled должен вернуть true.

1 Ответ

0 голосов
/ 10 февраля 2020

AsyncValidator немного проще, чем ваш код. (на самом деле вы не можете использовать valueChanges observable).

Представьте, что у вас есть dataService с двумя функциями, которые возвращают наблюдаемое true, если существует, и false, если нет. Ну, я симулирую, используя оператор "of" rx js и задержку

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. Валидаторы возвращают наблюдаемое, в случае ошибки наблюдаемое из объект {ошибка: 'пока существует'}, в случае, если допустимая наблюдаемая ноль.
  3. Начиная с dataService вы получаете истину -если существует-или ложь-если нет - я использую (pipe (map) для преобразования ответа и использую 'of' rx js для возврата наблюдаемой

Если вы хотите использовать задержку, потому что вам не нравится {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">
...