这就是我的做法。但是它仍然使用相当多的循环来检查和设置formarray中的formcontrols。
处理大数据时这仍然很糟糕。它只比为每个表单控件设置验证器快一点。
在我看来,以下两种方式是相似的。
方式一:
在 app.component.html 文件中:
<div [formGroup]="formArray">
<div class="px-3 py-1" *ngFor="let group of formArray.controls" [formGroup]="group">
Name <input formControlName="name"/>
Content <input formControlName="content" class="w-20" disabled/>
<small class="d-block text-danger"
*ngIf="group.get('name').errors?.duplicated">
This line is duplicated
</small>
</div>
</div>
在 app.component.ts 文件中:
import { Component } from '@angular/core';
import { FormGroup, FormControl, FormArray, ValidatorFn, ValidationErrors } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
formArray = new FormArray(
[
this.createGroup({ name: 'abc', content: '' }),
this.createGroup({ name: 'abc', content: '' }),
this.createGroup({ name: 'bcd', content: '' }),
this.createGroup({ name: 'bcd', content: '' }),
this.createGroup({ name: '', content: '' })
],
this.hasDuplicate('name')
);
createGroup(data: any) {
data = data || { name: null, content: null };
return new FormGroup({
name: new FormControl(data.name),
content: new FormControl(data.content)
});
}
duplicates = [];
hasDuplicate(key_form): ValidatorFn {
return (formArray: FormArray): { [key: string]: any } | null => {
if (this.duplicates) {
for (var i = 0; i < this.duplicates.length; i++) {
let errors = this.formArray.at(this.duplicates[i]).get(key_form).errors as Object || {};
delete errors['duplicated'];
this.formArray.at(this.duplicates[i]).get(key_form).setErrors(errors as ValidationErrors);
}
}
let dict = {};
formArray.value.forEach((item, index) => {
dict[item.name] = dict[item.name] || [];
dict[item.name].push(index);
});
let duplicates = [];
for (var key in dict) {
if (dict[key].length > 1) duplicates = duplicates.concat(dict[key]);
}
this.duplicates = duplicates;
for (const index of duplicates) {
formArray.at(+index).get(key_form).setErrors({ duplicated: true });
}
return duplicates.length > 0 ? { error: 'Has Duplicate !!!' } : null;
};
}
}
Link to Stackblitz
方式 2:
在 app.component.ts 文件中:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, FormArray, ValidationErrors } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
formArray = new FormArray([
this.createGroup({ name: 'abc', content: '' }),
this.createGroup({ name: 'abc', content: '' }),
this.createGroup({ name: 'bcd', content: '' }),
this.createGroup({ name: 'bcd', content: '' }),
this.createGroup({ name: '', content: '' })
]);
createGroup(data: any) {
data = data || { name: null, content: null };
return new FormGroup({
name: new FormControl(data.name),
content: new FormControl(data.content)
});
}
duplicates = [];
ngOnInit() {
setTimeout(() => {
this.checkDuplicates('name');
});
this.formArray.valueChanges.subscribe(x => {
this.checkDuplicates('name');
});
}
checkDuplicates(key_form) {
for (const index of this.duplicates) {
let errors = this.formArray.at(index).get(key_form).errors as Object || {};
delete errors['duplicated'];
this.formArray.at(index).get(key_form).setErrors(errors as ValidationErrors);
}
this.duplicates = [];
let dict = {};
this.formArray.value.forEach((item, index) => {
dict[item.name] = dict[item.name] || [];
dict[item.name].push(index);
});
for (var key in dict) {
if (dict[key].length > 1)
this.duplicates = this.duplicates.concat(dict[key]);
}
for (const index of this.duplicates) {
this.formArray.at(index).get(key_form).setErrors({ duplicated: true });
}
}
}
Link to Stackblitz