【问题标题】:Angular forms using custom validation and a dynamic value使用自定义验证和动态值的 Angular 表单
【发布时间】:2023-03-22 22:39:01
【问题描述】:

我正在尝试创建一个自定义的反应式表单验证,它允许我传递一个数据数组来检查一个字符串是否已经存在。我能够以一种方式做到这一点,因此这将是一个表单级别的验证,但我无法让它在单个表单控件上工作。

表单级验证

这将在整个表单上产生错误,而不仅仅是那个控件

this.myForm = this.fb.group({
  name: ['', Validators.compose([
    Validators.required,
  ]),
  ],
}, {
  validator: (formGroup: FormGroup) => this.checkStringExists(
    formGroup.controls.name,
    this.arrayOfStrings,
  ),
});

允许我接受表单控件并根据传入的数组检查它的自定义验证。

checkStringExists(formInput: AbstractControl, names: string[]): { [s: string]: boolean } {
  if (names && names.length && formInput && formInput.value) {
    const isUnique = !names.find((name) => name === formInput);
    if (isUnique) {
      return { nameExists: true };
    }
  }
  return null;
}

单独的控件验证(我想这样做的方式)

这只会在特定控件上产生错误

this.myForm = this.fb.group({
  name: ['', Validators.compose([
    Validators.required,
    this.checkStringExists(this.arrayOfStrings),
  ]),
  ],
});

允许我仅将数组作为 Validators.compose[] 的一部分的自定义验证 这里

checkStringExists(names: string[]): ValidatorFn {
  return (formInput: AbstractControl): ValidationErrors | null => {
    if (names && names.length && formInput && formInput.value) {
      const isUnique = !names.find((name) => name === formInput);
      if (!isUnique) {
        return { nameExists: true };
      }
    }
    return null;
  };
}

此订阅设置 arrayOfStrings 的值。

mySubscription.subscribe((value: string[]) => {
    this.arrayOfStrings = value;
  })

我遇到的问题是 arrayOfStrings 可能会更新多次。 如果我以第一种方式使用验证,则 arrayOfStrings 是最新的。如果我使用第二种验证方式,arrayOfStrings 为空/初始值。

我试图让这个验证以第二种方式工作,这样我就可以基于单个控件显示验证,而不是如果整个表单都有这个错误。有谁知道我如何传递这个值并保持最新?

我还在一个单独的帮助文件中包含验证函数,以便在整个应用程序中重复使用。

Here is an example

【问题讨论】:

    标签: angular angular-forms


    【解决方案1】:

    您的要求

    从您的帖子看来,您似乎有以下愿望清单:

    1. 外部验证功能(可在您的应用中重复使用)。
    2. 控制级别验证(以及,通过扩展,错误)而不是表单级别。
    3. 用于再次验证您的字段的动态值列表。

    解决方案

    以下内容是从您上面的问题中复制而来的:

    checkStringExists(formInput: AbstractControl, names: string[]): { [s: string]: boolean } {
      if (names && names.length && formInput && formInput.value) {
        const isUnique = !names.find((name) => name === formInput);
        if (isUnique) {
          return { nameExists: true };
        }
      }
      return null;
    }
    

    您可以在字段级别使用它,而不是在表单的验证器部分使用它,如下所示:

    // Contents could change later!
    arrayOfStrings: string[] = [];
    
    ...
    
    this.individualControlForm = this.fb.group({
      name: ['', Validators.compose([
        Validators.required,
        (control: AbstractControl) => ValidationHelper.checkStringExists(
              control, this.arrayOfStrings
        )]),
      ],
    });
    

    因为我们使用箭头函数作为我们的验证函数,所以我们从函数定义的范围继承this"call, apply and bind" section, here 的释义)。这(呵呵)意味着我们可以正常使用数组,每次调用验证函数时都使用最新的值。

    如果你想减少这个调用的大小,你可以使用.bind(this) 函数Eliseo mentioned in his answer。它会使事情变得更难阅读,但肯定会缩短创建表单的样板。选择你的毒药。

    旁注

    如果您确实需要使用整个表单验证(例如,如果您需要考虑多个字段来确定单个字段是否有效)但希望错误显示在您可以使用的字段中

    formData.form.controls['email'].setErrors({'incorrect': true});
    

    手动设置该特定字段的错误 (source)。

    【讨论】:

    • 谢谢,这太棒了,完全符合我的要求。我还可以对个人和表单级别使用相同的验证功能。我实际上曾尝试过这一点,但我传入了 FormGroup 而不是 AbstractControl,当它不起作用时,我离开了它。
    【解决方案2】:

    更新

    我的错!你可以传递一个数组,一个数组是一个不可变的值。所以如果你有

      export function findArray(array){
        return (control=>{
          return array.indexOf(control.value)<0?{error:'not match'}:null
        })
      }
    
      array=['one','two']
      control=new FormControl(null,findArray(this.array))
    

    你可以看到simple stackblitz

    真正的验证器不能有“动态”参数。所以有些人喜欢

    foolValidator(name:string)
    {
       return (control:AbstractControl)=>{
            return control.value!=name?{error:'it's not the name'}:null
       }
    }
    name="joe"
    control=new FormControl(null,foolValidator(this.name))
    

    只考虑“joe”,而不考虑变量“name”的名称

    您可以使用bind(this),绑定更改“范围”,参见例如this link

    foolValidator() //see that you not pass the argument
    {
       return (control:AbstractControl)=>{
            //see that you use "this.name"
            return control.value!=this.name?{error:'it's not the name'}:null
       }
    }
    name="joe"
    control=new FormControl(null,foolValidator().bind(this))
    

    【讨论】:

    • 抱歉,拖了这么久才回复,我被分配了一个急需的任务,忘记了这个。不幸的是, bind(this) 没有帮助,我忘了提到我的验证函数在一个单独的帮助文件中,绑定 this 不起作用,除非我使用错误。我能想到的唯一方法是将这个和一个值的字符串作为参数传递,并在函数中将它命名为 self self['arrayOfStrings']
    • 我的错!,如果你传递给函数一个数组必须工作。查看我的更新答案
    猜你喜欢
    • 2018-05-28
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    • 2018-12-29
    • 1970-01-01
    • 2019-07-28
    • 1970-01-01
    • 2018-04-01
    相关资源
    最近更新 更多