带有异步RxJS调用的Angular反应形式自定义验证器

时间:2019-05-23 18:57:01

标签: angular rxjs angular-reactive-forms

我意识到我的问题是我对可观察性和RxJS非常了解。

我有一个这样的自定义验证器:

export function ValidateFinalQty(service: MyService) {
    return (control: AbstractControl): { [key: string]: any } | null => {
        let qty = service.GetQty();
        if (control.value != qty) {
            return { FinalQuantityNotMatching: true };
        } else {
            return null;
        }
    };
}

GetQty返回一个RxJS Observable。 那么,如何设置它,以便我的同步验证器根据异步调用返回正确的值?我需要验证器的返回类型保持为{ [key: string]: any } | null

我看到了类似qty = await service.GetQty().first().toPromise();之类的建议,但是随后我返回了一个承诺,并且我无法为我所理解的验证者返回一个承诺。

我该如何处理?

来自我的package.json

"@angular/core": "7.1.0",
"@angular/forms": "7.1.0",
"rxjs": "6.3.3",
"rxjs-compat": "^6.4.0",

UPDATE 5/23/19尝试实现@Sachin的答案。 我在地图内的断点永远不会被击中。我没有任何控制台日志,即使我删除了映射中的逻辑并返回null,它仍然始终返回无效。对于这里发生的事情非常困惑。我已经确认了我的服务。

有什么想法吗?

export class CustomAsyncValidator {
    static ValidateFinalQty(qtyService: FinalQtyService, brf: BatchRecordForm): AsyncValidatorFn {
        return (control: AbstractControl) => {
            return qtyService.postCalcFinalQuanity(brf)
                .pipe(
                    map((qty) => {
                        console.log("running qty validator. value:", qty);
                        if (control.value !== qty) {
                            return { FinalQuantityNotMatching: true };
                        } else {
                            return null;
                        }
                    }),
                    catchError((err) => {
                        console.log("Error in final quantity validator", err);
                        return null;
                    }),
                    finalize(() => console.log("finished"))
                );
        };
    }
}

3 个答案:

答案 0 :(得分:1)

我有一种类似的Validator指令,请根据您的代码进行调整:

看看它是否对您有用

import { Directive } from '@angular/core';
import { NG_ASYNC_VALIDATORS, AsyncValidator, AbstractControl, ValidationErrors } from '@angular/forms';
import { MyService } from './MyService';
import { Observable,  of as observableOf} from 'rxjs';


@Directive({
  selector: '[qty-valid]',
  providers: [{provide: NG_ASYNC_VALIDATORS, useExisting: QuantityValidatorDirective , multi: true}]
})
export class QuantityValidatorDirective implements AsyncValidator {    
   constructor(private service : MyService ) { }

    validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {

        return new Promise((resolve, reject) => {                
                this.service.GetQty()
                    .subscribe( (qty: any) => {
                       if (control.value != qty) {
                          resolve({ FinalQuantityNotMatching: true })
                       } else {
                          resolve(null)
                       }

                },error => resolve(null));
        });
    }
}

答案 1 :(得分:1)

希望这会有所帮助

export class CustomAsyncValidator {
  static ValidateFinalQty(apiService: ApiService):AsyncValidatorFn {
    return (control: AbstractControl) => {
      const value = control.value;
      return apiService.GetQty()
        .pipe(
          take(1),
          map(qty => {
            if (value!=qty) {
              return { FinalQuantityNotMatching: true };
            } else {
              return null;
            }
          })
        );
    };
  }
}

使用方法

this.yourForm = this.fb.group({
  yourField: ["",CustomValidator.ValidateFinalQty(this.apiService)]
});

答案 2 :(得分:1)

在您的验证器发送异步RxJS调用时,最好使用AsyncValidator

(另请参阅我为您创建的有效的demo

从文档中

  

constructor(formState:any = null,validatorOrOpts ?: ValidatorFn | AbstractControlOptions | ValidatorFn [],asyncValidator ?: AsyncValidatorFn | AsyncValidatorFn [])

如您所见,您可以将AsyncValidatorFn作为第三个参数传递给FormControl。

假设getQty()返回的Observable发出您需要比较的值。您的自定义验证程序将如下所示:

custom.validator.ts

import { AbstractControl } from '@angular/forms';
import { MyService } from './my.service';
import { map } from 'rxjs/operators';
export class ValidateFinalQty {
  static createValidator(myService: MyService) {
    return (control: AbstractControl) => {
      return myService.getQty().pipe(map(res => {
        return control.value == res ? null :  { FinalQuantityNotMatching: true };
      }));
    };
  }
}

在这里我创建了一个静态方法,该方法将接受您的服务并执行调用,并在条件不匹配时给出错误。

我在您的代码中注意到的一件事是,不是订阅getQty()返回的Observable,而是将其值分配给qty变量,这不是处理Observable的正确方法。您可以了解有关可观察物here

的更多信息

现在在您的组件中:

import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms'
import { ValidateFinalQty } from './custom.validator';
import { MyService } from './my.service';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  implements OnInit{
  name = 'Angular';
  myForm: FormGroup

  constructor(private myService: MyService, private fb: FormBuilder) {

  }
  ngOnInit() {
    this.myForm = this.fb.group({
    quantity: [
      '',
      [Validators.required],
      ValidateFinalQty.createValidator(this.myService)
    ]
  });
}
}

并在您的HTML中:

<form [formGroup]="myForm">
  <label>Quantity</label>
  <input type="number" formControlName="quantity" placeholder="Enter quantity to validate">

  <div *ngIf="myForm.get('quantity').status === 'PENDING'">
    Checking...
  </div>
  <div *ngIf="myForm.get('quantity').status === 'VALID'">
    ? Quantity is valid!
  </div>

  <div *ngIf="myForm.get('quantity').errors && myForm.get('quantity').errors.FinalQuantityNotMatching">
    ? Oh noes, your quantity is not valid!
  </div>
</form>


<small>5 is valid quantity for the sake of example</small>

希望有帮助。