【问题标题】:Pass index into FormArray Control validator将索引传递给 FormArray 控件验证器
【发布时间】:2020-03-01 13:41:12
【问题描述】:

我有一个FormArray,输入很简单。我想通过验证器函数(自定义)FormArray 中的输入索引,因为我的验证器是基于此的。例如,我希望每个后续AbstractControl 的字母都比之前的多(这只是一个示例,传递索引是我真正需要实现的)。

我的意思是如果我能得到这样的东西

ngOnInit(): void {
    this.items = this.items?.length ? this.items : this.getDefaults(this.size)

    const inputs: FormArray = this.formGroup.get('inputs') as FormArray
        for (let item of this.items) {
            inputs.push(this.fb.control(
            item.value,
            this.validator(),
            this.asyncValidator(),
            ))
        }
}

private validator(): ValidatorFn {
    return (abstractControl: AbstractControl/*, indexInArray */) => {
        // const lenOfPrev = this.items[indexInArray - 1].value.length
        if (abstractControl.value.length <= 0 /* lenOfPrev */) {
            return {
                notLargeEnough: 'should be bigger'
            }
        }
        return null
    }
}

【问题讨论】:

  • 你能发布你当前的代码
  • @KurtHamilton 给你。评论部分是我想要工作的部分

标签: angular angular-reactive-forms


【解决方案1】:

您的目标是针对另一个控件验证一个控件。它们在表单数组中的事实有点无关紧要。

在编写自定义验证器时,您会受到其interface 的约束:

interface ValidatorFn {
  (control: AbstractControl): ValidationErrors | null
}

因此,如果您无法将另一个控件的引用注入到接口函数中,那么您唯一的选择就是将另一个控件注入到函数本身中。

  ngOnInit(): void {
    this.items = this.items?.length ? this.items : this.getDefaults(this.size)

    const inputs: FormArray = this.formGroup.get('inputs') as FormArray
    this.items.forEach((item, i) => {
      const previousControl = i > 0 ? inputs.controls[i - 1] : null;      
      inputs.push(this.fb.control(
        item.value,
        this.validator(),
        this.asyncValidator(),
      ));
    });    
  }

  private validator(previousControl: AbstractControl): ValidatorFn {
    return (control: AbstractControl) => {
      if (!previousControl || control.value.length > previousControl.value.length) {
        return null;
      }

      return {
        notLargeEnough: 'should be bigger'
      };
    }
  }

您必须处理第一个控件没有前一个控件可供比较的事实。

演示:https://stackblitz.com/edit/angular-9atrvn

您可能希望在值更改时添加一些更新相关控件验证的内容,因为默认情况下不会发生这种情况。

编辑

如果您的数组在其生命周期内发生更改,您可以使用FormControl.setValidators(validators) 更新受影响项目的验证器。

如果你从数组开始

A &lt;- B &lt;- C &lt;- D &lt;- E

并删除一个:

A &lt;- B &lt;- D &lt;- E

那么唯一改变的依赖是D -&gt; C 现在变成D -&gt; B。因此,您需要更新 D 的验证器以依赖于 B

注意:使用 setValidators 将覆盖所有现有的验证器。因此,如果您使用额外的验证器,也需要包含它们。

分叉演示:https://stackblitz.com/edit/angular-f93sgn

【讨论】:

  • 如果我在运行时添加/删除控件会怎样?
  • 除了以与模型相同的方式管理表单数组之外,您还需要在受影响的索引处修改受影响的控件。我已经分叉了我的 stackblitz 以展示我将如何删除单个项目。 stackblitz.com/edit/angular-f93sgn
  • 注意:使用setValidators 将覆盖所有现有的验证器。因此,如果您使用额外的验证器,也需要包含它们
  • 我已经编辑了我的答案以包含更多细节
【解决方案2】:

只需向函数 Validator 添加和参数

private validator(indexInArray:number): ValidatorFn { //<--here
    return (abstractControl: AbstractControl) => {
      //you can use indexInArray here
     if (indexInArray==0)  //For the first give always valid
         return null; 
      //I change this.items by this.form.value
      const lenOfPrev = this.form.value[indexInArray - 1].value.length
      if (abstractControl.value.length <= lenOfPrev.length) {
        return {
          notLargeEnough: 'should be bigger'
        }
      }
      return null
    }
  }

但是,如果您使用的是组件的值,则需要“绑定”验证器

this.items.forEach((item, i) => {
      const previousControl = i > 0 ? inputs.controls[i - 1] : null;      
      inputs.push(this.fb.control(
        item.value,
        this.validator(i).bind(this), //<--see the argument and the "bind"
        this.asyncValidator(),
      ));
    }); 

但是,对不起。这种方法有一个相关的错误,如果你改变会发生什么,例如从“aaa”到“aaaaa”的第二个输入?。如果您的第三个输入是 'bbbb',则 before 有效,但 change 之后无效。但是 angualr 可以考虑到这一点,因为 3rt 输入没有改变。那么,为什么不在所有 formArray 上使用 Validator。作为自定义验证器返回一个对象,您可以返回,例如一个数组 [false,false,true,false] 并询问这个值

  private validatorArray()
  {
    return (formArray:FormArray)=>{
      if (!formArray.value)
        return null;
      const result= (formArray.value.map((x:any,i:number)=>{
        if (i==0)
           return false;
        return (x.length<=formArray.value[i-1].length)
      }))
      return result.find(x=>x)?{error:result}:null;
    }
  }

form.errors?.error[i] 中,如果控件无效,您有

你可以在this stackblitz看到这两种方法

注意:我直接使用 FormControls 的 formArray,但它是一样的

更新 看完 Kurt 的回答后,真的不需要在验证器中bind(this),只要按照 Kurt 说的传递之前的控制即可。

【讨论】:

  • 将validator绑定到FormArray,然后设置children的有效性,其实是个有趣的想法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-22
  • 2021-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多