这晚了几个月,但我想我会根据以下内容提供我的解决方案这里的教程。其要点是,一旦改变处理表单的方式,管理就会容易得多。
首先,使用ReactiveFormsModule
代替或补充正常的FormsModule
。使用反应式表单,您可以在组件/服务中创建表单,然后将它们插入到您的页面中,而不是页面本身生成表单。它的代码有点多,但更容易测试,更灵活,而且据我所知,制作许多不平凡的表单的最佳方法。
从概念上讲,最终结果看起来有点像这样:
你有一个基地FormGroup
与任何FormControl
整个表格所需的实例。例如,正如我链接到的教程中所示,假设您想要一个表单,用户可以在其中输入一次姓名,然后输入任意数量的地址。所有一次性字段输入都将位于该基本表单组中。
里面那个FormGroup
实例将有一个或多个FormArray
实例。 AFormArray
基本上是一种将多个控件组合在一起并迭代它们的方法。也可以放多个FormGroup
数组中的实例,并将它们用作本质上嵌套在较大表单中的“迷你表单”。
通过嵌套多个FormGroup
and/or FormControl
动态中的实例FormArray
,您可以控制有效性并将表单作为由多个动态部分组成的一个大的反应性部分进行管理。例如,如果您想在允许用户提交之前检查每个输入是否有效,则一个子表单的有效性将“冒泡”到顶级表单,整个表单将变得无效,从而很容易管理动态输入。
As a FormArray
本质上,它是数组接口的包装器,但对于表单片段,您可以随时推送、弹出、插入和删除控件,而无需重新创建表单或进行复杂的交互。
如果我链接到的教程失败了,这里有一些您可以自己实现的示例代码(我的示例使用 TypeScript)来说明基本思想:
基础组件代码:
import { Component, Input, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'my-form-component',
templateUrl: './my-form.component.html'
})
export class MyFormComponent implements OnInit {
@Input() inputArray: ArrayType[];
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
let newForm = this.fb.group({
appearsOnce: ['InitialValue', [Validators.required, Validators.maxLength(25)]],
formArray: this.fb.array([])
});
const arrayControl = <FormArray>newForm.controls['formArray'];
this.inputArray.forEach(item => {
let newGroup = this.fb.group({
itemPropertyOne: ['InitialValue', [Validators.required]],
itemPropertyTwo: ['InitialValue', [Validators.minLength(5), Validators.maxLength(20)]]
});
arrayControl.push(newGroup);
});
this.myForm = newForm;
}
addInput(): void {
const arrayControl = <FormArray>this.myForm.controls['formArray'];
let newGroup = this.fb.group({
/* Fill this in identically to the one in ngOnInit */
});
arrayControl.push(newGroup);
}
delInput(index: number): void {
const arrayControl = <FormArray>this.myForm.controls['formArray'];
arrayControl.removeAt(index);
}
onSubmit(): void {
console.log(this.myForm.value);
// Your form value is outputted as a JavaScript object.
// Parse it as JSON or take the values necessary to use as you like
}
}
子组件代码:(每个新输入字段一个,以保持整洁)
import { Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'my-form-sub-component',
templateUrl: './my-form-sub-component.html'
})
export class MyFormSubComponent {
@Input() myForm: FormGroup; // This component is passed a FormGroup from the base component template
}
基本组件 HTML
<form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate>
<label>Appears Once:</label>
<input type="text" formControlName="appearsOnce" />
<div formArrayName="formArray">
<div *ngFor="let control of myForm.controls['formArray'].controls; let i = index">
<button type="button" (click)="delInput(i)">Delete</button>
<my-form-sub-component [myForm]="myForm.controls.formArray.controls[i]"></my-form-sub-component>
</div>
</div>
<button type="button" (click)="addInput()">Add</button>
<button type="submit" [disabled]="!myForm.valid">Save</button>
</form>
子组件 HTML
<div [formGroup]="form">
<label>Property One: </label>
<input type="text" formControlName="propertyOne"/>
<label >Property Two: </label>
<input type="number" formControlName="propertyTwo"/>
</div>
在上面的代码中,我基本上有一个代表表单基础的组件,然后每个子组件管理自己的FormGroup
内的实例FormArray
位于基地内部FormGroup
。基本模板将子组传递到子组件,然后您可以动态处理整个表单的验证。
此外,这使得通过策略性地在表单中插入和删除组件来重新排序组件变得很简单。它适用于(看似)任意数量的输入,因为它们不与名称冲突(据我所知,模板驱动表单的一个很大缺点),并且您仍然保留几乎自动验证。这种方法的唯一“缺点”是,除了编写更多代码之外,您还必须重新学习表单的工作原理。然而,随着您的继续,这将为更大、更动态的形式提供可能性。
如果您有任何疑问或想指出一些错误,请继续。我只是根据上周我自己做的事情输入了上面的代码,更改了名称和其他杂项。省略了属性,但它应该很简单。上面的代码和我自己的代码之间的唯一主要区别是,我将所有表单构建移至从组件调用的单独服务中,因此不会那么混乱。