Angular 11 反应形式 onchange 验证以检查重复值

Angular11 reactive form on change validate to check for duplicate value

我正在使用 Angular 11 并使用 formcontrolname 'name' 创建一个简单的响应式表单。 当用户在此字段中键入时,我需要验证唯一性。我尝试了以下操作,但每次我输入内容时它都会验证,但我想使用 debouncetime 并使用类似的逻辑。不确定如何用反应形式做到这一点

谁能帮我实现这个目标?

我最终得到以下 AsyncVaildator。如果可以简化,有人可以帮助我吗?因为我正在将服务传递给方法。有没有办法在这里使用依赖注入?

export class TemplateNameValidator {
    createValidator(auditTemplateService: AuditTemplateService): AsyncValidatorFn {
      console.log("Static factory call");
      
      return (control: AbstractControl): Observable<ValidationErrors> => {
        if(isEmptyInputValue(control.value)) {
            return of(null);
        } else {
            return control.valueChanges.pipe(
                debounceTime(500),
                distinctUntilChanged(),
                take(1),
                switchMap((name: string) => 
                    auditTemplateService.isNameUnique(name)
                        .pipe(
                            map(isUnique => !isUnique ? { 'duplicate': true } : null)
                        )
                )
            );
        }
      };
    }
  }

  function isEmptyInputValue(value: any): boolean {
      return value === null || value.length === 0;
  }



private registerFormGroup(): void {
        this.nameField = new FormControl(
            { value: this.auditTemplate.title, disabled: true },
            [Validators.compose([
                Validators.required,
                (control) => this.isNameUnique(control as AbstractControl)
            ])]
        );

        this.templateForm = this.formBuilder.group({
            templateName: this.nameField,
            tags: [this.auditTemplate.tags]
        });
    }

检查唯一性的验证:

isNameUnique(formField: AbstractControl): { [key: string] : any} {
        const nameEntered = formField.value;
        let isDuplicate = false;
        if(nameEntered && this.availableNames) {
            const index = this.availableNames.findIndex(templateName => 
                        templateName.name === nameEntered);
            if(index !== -1) {
                isDuplicate = true;
            }
        }
        return isDuplicate ? { 'duplicate': true } : null;
    }

谢谢

我确信这个问题有很多解决方案,简而言之,文档 https://angular.io/guide/form-validation#implementing-a-custom-async-validator.

中对此进行了描述

但是,如何使用异步验证器并不是很明显。所以一个最小的例子可能是这样的:

const myValidator: AsyncValidatorFn = (control: AbstractControl) => of(control.value)
  .pipe(
    delay(1000),
    map(value => {
      if (value === '1') {
        return null;
      }
      return { invalid: true } as ValidationErrors;
    }),
    tap(console.log),
    finalize(() => console.log('async validator unsubscribed')),
  );

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  control = new FormControl('', Validators.required, myValidator);
}

现场演示:https://stackblitz.com/edit/angular-ivy-mk4wyv?file=src%2Fapp%2Fapp.component.ts

验证器从 control 中获取最新值,延迟它然后执行一些检查。在这种情况下,仅当您在输入字段中键入 "1" 时验证器才会通过。

请注意,去抖是由 delay() 执行的,没有使用 debounceTime() 运算符。这是因为在每次控制值更改时都会调用验证方法,并且需要 return 发出并完成的 Observable。如果有另一个待验证的 Observable,Angular 将取消订阅它并订阅新的 Observable。这就是为什么您会在控制台中看到许多 'async validator unsubscribed' 日志。

最后一件事,异步验证器作为另一个参数传递给 FormControl,而不是在常规验证器中:

new FormControl('', Validators.required, myValidator)