我有一个包含Formarray的Formgroup。
这就是结构:
myForm = this.fb.group(
{
title: ["", [Validators.required, Validators.minLength(4)]],
pairs: this.fb.array(
this.fPairs.map(f =>
this.fb.group({
grade: [],
value: []
})
)
)
}
);
我的FormArray被映射,看起来像这样onUnit:
fPairs: Array<pairs> = [
{grade: 0, value: 0},
{grade: 0, value: 0},
{grade: 0, value: 0},
{grade: 0, value: 0}
];
我想要实现的是,因为这个FormArray的每个对象的每个属性都是我表单中的一个输入字段,所以我需要一个这样的验证:
索引0处的对象属性必须具有比下一个索引更大的值。
因此在myForm
中,
pairs[0].score > pairs[1].score
pairs[1].score > pairs[2].score
pairs[2].score > pairs[3].score
属性"value"也是如此。
如何正确实现此formArray
的真实验证器(类型为ValidatorFn
(?
到目前为止,我只创建了一个函数,检查每个字段,将其与上一个和下一个字段进行比较,如果值不符合规则,我会手动用setErrors()
设置错误
这个函数在ValueChanges()
订阅中,所以当formArray
中的值发生变化时,它会用我的函数进行检查
有更好的方法吗?
这里有一个stackblitz(valueChanges
订阅无法正常工作,只有在下一个字段中写入时才会刷新,你会在stackblitz中看到我的意思(
https://stackblitz.com/edit/angular-mat-formfield-flex-layout-x9nksb
谢谢
所以,过了一段时间(很抱歉延迟了(,我制作了一个stackblitz来复制您的最小示例,并为您制作了验证程序。代码在我答案的末尾。
简单地向您解释一下:交叉控制验证必须在父级中进行。父级可以是窗体组,也可以是窗体数组。在您的情况下,这将是一个表单数组(包含您所有分数的数组(。
然后,这些错误将直接附加到表单数组中,从而在满足条件时使其无效。
正如你所看到的,控制者根本不知道他们的错误:我自愿这么做,所以我不为你工作。我的最终目标是向您展示如何比较两个字段并相应地设置表单错误。
现在,在验证器中,如果您愿意,您可以从控件中添加/删除错误,但我想我已经回答了您关于在两个独立字段上进行表单验证的第一个问题。
代码是自我解释的,但如果您有问题,请随时询问!
(附言:如果你想看到错误,错误会显示在stackblitz页面的页脚中(
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, ValidatorFn } from '@angular/forms';
@Component({
selector: 'my-app',
template: `
<form novalidate [formGroup]="form" fxLayout="column" fxLayoutGap="24px">
<div formArrayName="pairs" fxLayout="row" fxLayoutGap="12px" *ngFor="let pair of form.get('pairs').controls; let i = index">
<ng-container [formGroupName]="i">
<mat-form-field fxFlex="50%">
<input matInput type="text" formControlName="grade" placeholder="Grade for {{ i }}">
</mat-form-field>
<mat-form-field fxFlex="50%">
<input matInput type="text" formControlName="value" placeholder="Score for {{ i }}">
</mat-form-field>
</ng-container>
</div>
</form>
<p>The form is {{ form.invalid ? 'invalid' : 'valid' }}</p>
<p>The pairs group is {{ form.get('pairs').invalid ? 'invalid' : 'valid' }}</p>
<p>Errors on the form : {{ form.errors | json }}</p>
<p>Errors on the group : {{ form.get('pairs').errors | json }}</p>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
form: FormGroup;
// Create a dataset
data = [
{ grade: 6, value: 0 },
{ grade: 5, value: 0 },
{ grade: 4, value: 0 },
{ grade: 3, value: 0 },
];
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
// Create a form group
this.form = this.fb.group({
// Create a form array made of form groups
pairs: this.fb.array(this.data.map(item => this.fb.group(item)))
});
// Add validators (optional, used to split the code logic)
this.addValidators();
}
addValidators() {
// Get the form array and append a validator (again code split)
(this.form.get('pairs') as FormArray).setValidators(this.formArrayValidator());
}
// Form validator
formArrayValidator(): ValidatorFn {
// The validator is on the array, so the AbstractControl is of type FormArray
return (group: FormArray) => {
// Create an object of errors to return
const errors = {};
// Get the list of controls in the array (which are FormGroups)
const controls = group.controls;
// Iterate over them
for (let i = 1; i < controls.length; i++) {
// Get references to controls to compare them (split code again)
const valueControl = controls[i].get('value');
const previousValueControl = controls[i - 1].get('value');
// if error, set array error
if (valueControl.value > previousValueControl.value) {
// array error (sum up of all errors)
errors[i + 'greaterThan' + (i - 1)] = true;
}
}
// return array errors ({} is considered an error so return null if it is the case)
return errors === {} ? null : errors;
}
}
}