我为我的表单创建了一个定制验证器.它应该判断在特定输入字段中输入的值是否已经出现在本地数组中. 由于该数组定期更新,但在创建时(当我创建表单并声明验证器时)仍然为空,因此我创建了该数组的Observable,以将其作为参数传递给验证器.但在验证器端I never get any value上,即使数组有一些.

你能帮我找出我做错了什么吗?

values: RowData[] = []; // The values array which is regularly updated
values$: Observable<RowData[]>;

constructor() {
    this.values$ = of(this.values); // Observable of the array

    // Build form
    this.myForm = this.formBuilder.group({
      id: [
        '',
        checkIdUniqueValidator(this.generalWellsIds$),
      ]
    });
  }

下面是自定义验证器:

export function checkIdUniqueValidator(existingValues$: Observable<RowData[]>): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    let existingValues: Array<string>;

    // Here I try to get values from the Observable
    const tempSub = existingValues$.subscribe(x => existingValues = x.map(y => y.id));

    const valueAlreadyExists = existingValues 
      && existingValues.length > 0 
      && existingValues.includes(control.value);

    tempSub.unsubscribe();

    return valueAlreadyExists
      ? {duplicate: {value: control.value}}
      : null;
  };
}

推荐答案

AsyncValidator的方法在您的情况下效果很好.您可以将验证器函数转换为如下所示:

export function checkIdUniqueValidator(
  existingValues$: Observable<RowData[]>
): AsyncValidatorFn {
  return (control: AbstractControl) =>
    existingValues$.pipe(
      map((rowDataList) => rowDataList.map((rd) => rd.id)),
      map((existingValues) =>
        existingValues.includes(control.value)
          ? { duplicate: { value: control.value } }
          : null
      ),
      first()
    );
}

然后为了使用它,您应该使用FormControl构造函数来传递它,如下所示:

this.myForm = this.formBuilder.group({
  id: new FormControl('', {
    validators: [Validators.required],
    asyncValidators: [checkIdUniqueValidator(this.valuesSource$)]
  })
});

this.valuesSource$个可观察值实际上应该是某种类型的Subject,这样每当values数组更改时(当您将push放入其中或从中移除元素时),您就可以发出新值.

values: RowData[] = []; // The values array which is regularly updated
valuesSource$ = new BehaviorSubject(this.values);

如果你这样放着,它不会被更新.您的可观察对象只会发出一次空数组,仅此而已.除非您在每次修改this.values数组时调用this.valuesSource$.next(this.values);,否则您永远不会有已经使用过的ID.

我模拟了带有超时的this.values数组的更新,它似乎如预期的那样工作.

ngOnInit(): void {
  setTimeout(() => {
    this.values.push({ id: '1' });
    this.values.push({ id: '2' });
    this.values.push({ id: '3' });
    this.valuesSource$.next(this.values);
  }, 3000);
}

Angular相关问答推荐

导入浏览器模块时出错已导入commonModule的insted,animationBrower也显示错误,当p打开时

无法执行客户端功能Angular 17与ssr'

接收错误NullInjectorError:R3InjectorError(_AppModule)[config—config]....>过度安装Angular—Markdown—Editor

Angular 15:将数据从主零部件传递到辅零部件

倒计时计时器,每10秒重新启动一次,共4次

在Angular中扩展多个类

Nx Angular Monorepo 中 cypress 组件测试的代码覆盖率?

iss 声明无效 Keycloak

使用 BehaviorSubject 从服务更新 Angular 组件

RxJS 基于先前的数据响应执行请求(涉及循环对象数组)

在更改检测之前调用创建的间谍

Angular模板错误:Property does not exist on type component

如何使用ngrx和effects 订阅成功回调

运行 npm 测试(Angular 2 单元测试)后无法读取未定义的属性 subscribe

由于时区,Angular 4 日期管道显示错误的日期 - 如何解决这个问题?

请求 http.GET 时发送的 Angular2 OPTIONS 方法

缺少带有Angular的语言环境XXX的语言环境数据

Angular 应用程序必须在新部署后清除缓存

使用 EventEmitter 传递参数

如何在Angular 2中将对象从一个组件传递到另一个组件?