动态和隐藏字段的反应式表单验证
Reactive form validation for dynamic and hidden fields
首先,我有一个 Angular 响应式表单,它有一个按钮可以向表单添加另一个 FormArray
。所有验证都很好并且按预期工作。当向已经动态的表单组引入另一个动态控件时,事情变得有点棘手。此控件是 shown/hidden 基于 selection 在另一个表单控件中制作的。
当控件显示时我引入验证,当控件隐藏时验证被清除。这确保我的表格保持 valid/invalid 正确。
它有点像马车当我完成一组输入并添加另一组动态输入时,两者都会触发隐藏控件......然后修改先前的隐藏输入 - 表单保持不变。例如
选择 123
会触发“其他代码”控件,删除该值应该会使表单无效,但此时它仍然有效。
我有一个更改功能分配给 select 以确定 select 编辑了什么。
selectClick(x) {
const f = this.form;
let items = this.form.get('justificationItems') as FormArray;
if (x === '123') {
items.controls.forEach(i => {
console.log(i)
i['controls'].other.setValidators([Validators.required]);
// i['controls'].other.updateValueAndValidity();
});
} else {
items.controls.forEach(i => {
i['controls'].other.clearValidators();
// i['controls'].other.updateValueAndValidity();
});
}
f.updateValueAndValidity();
}
我怀疑在更改 select 属性 以触发上述内容时,它没有对正确的索引项执行此操作,而是对所有索引项执行此操作?
StackBlitz - https://stackblitz.com/edit/angular-prepopulate-dynamic-reactive-form-array-ufxsf9?file=src/app/app.component.ts
根本原因:当selectClick
触发时,您以数组形式清除或设置所有控件other
的验证。您应该只为 formArray 中的一种形式设置。
我重写你的函数:
selectClick(x, index) {
const f = this.form;
let items = this.form.get('justificationItems') as FormArray;
if (x === '123') {
items.controls[index]['controls'].other.setValidators([Validators.required]);
} else {
items.controls.forEach(i => {
items.controls[index]['controls'].other.clearValidators();
i['controls'].other.updateValueAndValidity();
});
}
items.controls[index]['controls'].other.updateValueAndValidity();
}
更改模板中的代码:
<select
(change)="selectClick($event.target.value, i)"
formControlName="code"
>
“clear/add 验证器”的最佳方式确实是启用或禁用 formControls。请记住 formControl 具有以下状态之一:
type FormControlStatus = 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED';
因此我们可以简化 enabled/disabled FormControl。此外,当我们创建 formGroup 时,我们可以创建禁用的,所以首先将 not INVALID
嗯,代码有点乱。首先使用 i['controls'].other(你真的可以使用 i.controls.other
一个使用 FormBuilder 和新 Form 的陌生组合。
一如既往,我们有一个 FormArray,我们创建一个 getter
get justificationItems()
{
return this.form.get('justificationItems') as FormArray;
}
代替使用两个不同的函数来创建表单,我们可以使用 和 unique
createJustificationField(x: any=null): FormGroup {
x=x || {name:null,description:null,code:null,other:null}
return new FormGroup({
name: new FormControl(x.name, [Validators.required]),
description: new FormControl(x.description, [Validators.required]),
code: new FormControl(x.code, [Validators.required]),
other: new FormControl({value:x.other,
disabled:x.code!=='123'},[Validators.required]),
});
}
看到我们可以使用 as
this.createJustificationField(..an object..)
this.createJustificationField()
我们的函数:createForm
、addItem
和selectClick
(我更喜欢另一个名字,比如codeChange
,但只是一个小改动)变成了
createForm() {
this.service.getmodel().subscribe((response:any) => {
this.form = new FormGroup({
justificationItems: new FormArray(
response.justificationItems.map(x=>
this.createJustificationField(x))
),
});
});
}
addItem(): void {
this.justificationItems.push(this.createJustificationField());
this.form.updateValueAndValidity();
}
selectClick(x,i) {
if (x === '123')
this.justificationItems.at(i).get('other').enable()
else
this.justificationItems.at(i).get('other').disable()
this.form.updateValueAndValidity();
}
而 .html 变得更加清晰
<form *ngIf="form" [formGroup]="form">
<div formArrayName="justificationItems">
<div
*ngFor="
let orgs of justificationItems.controls;
let i = index;
let last = last
"
[formGroupName]="i"
>
<label>Name </label>
<input formControlName="name" placeholder="Item name" /><br />
<label>Description </label>
<input
formControlName="description"
placeholder="Item description"
/><br />
<label>Code </label>
<select
(change)="selectClick($event.target.value, i)"
formControlName="code"
>
<option value="123">123</option>
<option value="456">456</option></select
><br />
<ng-container *ngIf="justificationItems.at(i).value.code === '123'">
<label>Other Code </label>
<input formControlName="other" placeholder="other" /><br /><br />
</ng-container>
<button
*ngIf="last"
[disabled]="justificationItems.at(i).invalid"
type="button"
(click)="addItem()"
>
Add Item
</button>
</div>
</div>
<button [disabled]="!form.valid" type="button">Submit</button>
</form>
<p>Is form valid: {{ form?.valid | json }}</p>
查看 stackblitz
首先,我有一个 Angular 响应式表单,它有一个按钮可以向表单添加另一个 FormArray
。所有验证都很好并且按预期工作。当向已经动态的表单组引入另一个动态控件时,事情变得有点棘手。此控件是 shown/hidden 基于 selection 在另一个表单控件中制作的。
当控件显示时我引入验证,当控件隐藏时验证被清除。这确保我的表格保持 valid/invalid 正确。
它有点像马车当我完成一组输入并添加另一组动态输入时,两者都会触发隐藏控件......然后修改先前的隐藏输入 - 表单保持不变。例如
选择 123
会触发“其他代码”控件,删除该值应该会使表单无效,但此时它仍然有效。
我有一个更改功能分配给 select 以确定 select 编辑了什么。
selectClick(x) {
const f = this.form;
let items = this.form.get('justificationItems') as FormArray;
if (x === '123') {
items.controls.forEach(i => {
console.log(i)
i['controls'].other.setValidators([Validators.required]);
// i['controls'].other.updateValueAndValidity();
});
} else {
items.controls.forEach(i => {
i['controls'].other.clearValidators();
// i['controls'].other.updateValueAndValidity();
});
}
f.updateValueAndValidity();
}
我怀疑在更改 select 属性 以触发上述内容时,它没有对正确的索引项执行此操作,而是对所有索引项执行此操作?
StackBlitz - https://stackblitz.com/edit/angular-prepopulate-dynamic-reactive-form-array-ufxsf9?file=src/app/app.component.ts
根本原因:当selectClick
触发时,您以数组形式清除或设置所有控件other
的验证。您应该只为 formArray 中的一种形式设置。
我重写你的函数:
selectClick(x, index) {
const f = this.form;
let items = this.form.get('justificationItems') as FormArray;
if (x === '123') {
items.controls[index]['controls'].other.setValidators([Validators.required]);
} else {
items.controls.forEach(i => {
items.controls[index]['controls'].other.clearValidators();
i['controls'].other.updateValueAndValidity();
});
}
items.controls[index]['controls'].other.updateValueAndValidity();
}
更改模板中的代码:
<select
(change)="selectClick($event.target.value, i)"
formControlName="code"
>
“clear/add 验证器”的最佳方式确实是启用或禁用 formControls。请记住 formControl 具有以下状态之一:
type FormControlStatus = 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED';
因此我们可以简化 enabled/disabled FormControl。此外,当我们创建 formGroup 时,我们可以创建禁用的,所以首先将 not INVALID
嗯,代码有点乱。首先使用 i['controls'].other(你真的可以使用 i.controls.other
一个使用 FormBuilder 和新 Form 的陌生组合。
一如既往,我们有一个 FormArray,我们创建一个 getter
get justificationItems()
{
return this.form.get('justificationItems') as FormArray;
}
代替使用两个不同的函数来创建表单,我们可以使用 和 unique
createJustificationField(x: any=null): FormGroup {
x=x || {name:null,description:null,code:null,other:null}
return new FormGroup({
name: new FormControl(x.name, [Validators.required]),
description: new FormControl(x.description, [Validators.required]),
code: new FormControl(x.code, [Validators.required]),
other: new FormControl({value:x.other,
disabled:x.code!=='123'},[Validators.required]),
});
}
看到我们可以使用 as
this.createJustificationField(..an object..)
this.createJustificationField()
我们的函数:createForm
、addItem
和selectClick
(我更喜欢另一个名字,比如codeChange
,但只是一个小改动)变成了
createForm() {
this.service.getmodel().subscribe((response:any) => {
this.form = new FormGroup({
justificationItems: new FormArray(
response.justificationItems.map(x=>
this.createJustificationField(x))
),
});
});
}
addItem(): void {
this.justificationItems.push(this.createJustificationField());
this.form.updateValueAndValidity();
}
selectClick(x,i) {
if (x === '123')
this.justificationItems.at(i).get('other').enable()
else
this.justificationItems.at(i).get('other').disable()
this.form.updateValueAndValidity();
}
而 .html 变得更加清晰
<form *ngIf="form" [formGroup]="form">
<div formArrayName="justificationItems">
<div
*ngFor="
let orgs of justificationItems.controls;
let i = index;
let last = last
"
[formGroupName]="i"
>
<label>Name </label>
<input formControlName="name" placeholder="Item name" /><br />
<label>Description </label>
<input
formControlName="description"
placeholder="Item description"
/><br />
<label>Code </label>
<select
(change)="selectClick($event.target.value, i)"
formControlName="code"
>
<option value="123">123</option>
<option value="456">456</option></select
><br />
<ng-container *ngIf="justificationItems.at(i).value.code === '123'">
<label>Other Code </label>
<input formControlName="other" placeholder="other" /><br /><br />
</ng-container>
<button
*ngIf="last"
[disabled]="justificationItems.at(i).invalid"
type="button"
(click)="addItem()"
>
Add Item
</button>
</div>
</div>
<button [disabled]="!form.valid" type="button">Submit</button>
</form>
<p>Is form valid: {{ form?.valid | json }}</p>
查看 stackblitz