将索引传递到FormArray控件验证器



我有一个带有简单输入的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
}
}

只需向函数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",则在更改之前有效,但在更改之后无效。但语言可以考虑到这一点,因为第三次输入没有改变。那么,为什么不在所有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]中,如果控件是无效的

你可以在这个堆叠的中看到这两个

注意:我直接使用FormControls的formArray,但它是相同的

Upate在阅读了Kurt的答案后,确实没有必要在验证器中绑定(this(,只需像Kurt所说的那样传递之前的控制即可。

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

在编写自定义验证器时,您受其接口的约束:

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 <- B <- C <- D <- E

并删除一个:

A <- B <- D <- E

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

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

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

最新更新