Angular 2 使用嵌套组件创建反应式表单
Posted
技术标签:
【中文标题】Angular 2 使用嵌套组件创建反应式表单【英文标题】:Angular 2 creating reactive forms with nested components 【发布时间】:2017-07-20 18:46:54 【问题描述】:我的要求是我需要创建一个带有嵌套组件的表单。
我正在为每个表单字段创建组件,这意味着文本框将
是一个组件,对于单选按钮会有另一个组件。<form [formGroup]="myForm">
<textbox-component></textbox-component>
<radioButton-component></radioButton-component>
</form>
我想使用响应式表单来创建我想要的表单 html 保持不变,并且仅通过 typescript 进行表单验证。
但我找不到任何解决方案,我们如何才能嵌套反应式表单 组件。
【问题讨论】:
也许每个组件都会保留自己的验证器。在提交时,每个组件通过服务或事件发射器向父组件发出值...未测试,但这应该可以工作 我无法在子组件内的 html 中添加 formControlName 属性(反应式表单所必需的),它抛出父指令 fromGroup 不存在的错误(因为它存在于父) 这就是为什么每个组件都应该保留自己的 formControlName 和验证器的原因。父组件角色将实例化表单并在提交时获取所有数据。子组件将触发有效或错误事件并通知父组件是否允许提交...看看Nested Model Driven Form 我看过这篇文章,但它是一个非常静态的例子。我通过动态渲染组件来生成表单。搜索了网络,但除了这篇文章之外没有找到任何帮助。我认为 Angular 2 处于非常初始阶段,也没有适当的文档。那么如何为 Angular 2 中的新功能寻求帮助呢? 【参考方案1】:在我的研究和实验之后,我找到了我的问题的一个答案,所以我自己来回答。如果它节省了某人的时间,那么我会很高兴。
如果您想使用嵌套组件创建反应式表单,那么您可以执行以下操作
在这里,我正在创建一个包含两个嵌套组件的表单,一个用于文本框,另一个用于单选按钮
你的父组件可以是这样的
<form [formGroup]="myForm">
<child-textbox-component [parentFormGroup]="myForm">
</child-textbox-component>
<child-radio-button-component [parentFormGroup]="myForm">
</child-radio-button-component>
</form>
我们将 FormGroup 对象作为输入传递给在父组件中创建的子组件 作为子组件的输入,他们将在他们的子组件中使用这个 FormGroup 对象 组件来设计类的特定控件
你的子组件会是这样的
子文本框组件
<div class="form-group" [formGroup]="parentFormGroup">
<label>
control.caption
</label>
<input class="form-control" type="text" [title]="control.toolTip"
[attr.maxlength]="control.width" [name]="control.name"
[value]="control.defaultValue" [formControlName]="control.name"/>
</div>
子单选按钮组件
<div class="form-group" [formGroup]="parentFormGroup">
<label>
control.caption
</label>
<div>
<label *ngFor="let value of control.values; let idx = index"
class="radio-inline" [title]="control.tooltip">
<input type="radio" [name]="control.name" [formControlName]="control.name"/>
value
</label>
</div>
</div>
这里的控制是模型类保存要显示的数据 子组件。
这样您就可以使用嵌套组件生成表单, 这样您就不必将表格(可以说是大表格)单独保存 零件。您可以将其分解为尽可能多的子组件和表单 也可以使用 Angular 2 的反应形式轻松创建和维护。您也可以轻松添加验证。
在回答这个问题之前我点击了这些链接
something similar on ***
angular 2 dynamic forms
【讨论】:
您如何在子组件中进行验证?我们可以在子组件中添加验证或在父组件中定义所有验证吗?您可以发布您的 .ts 文件代码 这个答案看起来很有用,但并不完整。你能把你的TS代码贴出来吗?即,设置 FormGroup 对象及其控件的方式和位置。 你可以按照这个教程这个很棒的toddmotto.com/… 我根据这个解决方案做了一个简单的例子:stackblitz.com/edit/angular-1drscm(基于coryrylan.com/blog/…的switch示例)。 只有我的两分钱。我刚刚使用这种方法处理了一个现有的 Angular 项目,您将表单作为@Input/@Output 传递,维护它真的很痛苦。【参考方案2】:对 Mhesh 的回答的附加说明,您可以构建这个相同的解决方案,而无需在 HTML 中注入 [parentFormGroup]
。您可以通过在可重用表单组上关注此 Stack Overflow post 来做到这一点。
这真是太好了。
示例
要采用现有的解决方案,你可以做同样的事情,除了:
你的父组件可以这样,不需要传入任何额外的参数
<form [formGroup]="myForm">
<child-textbox-component></child-textbox-component>
<child-radio-button-component></child-radio-button-component>
</form>
另外请注意,您可以像这样设置表单组:
<form [formGroup]="myForm">
<child-textbox-component></child-textbox-component>
<child-radio-button-component formGroupName="myGroup"></child-radio-button-component>
</form>
子文本框组件
<div class="form-group" [formGroup]="controlContainer.control">
<label>
control.caption
</label>
<input class="form-control" type="text" [title]="control.toolTip"
[attr.maxlength]="control.width" [name]="control.name"
[value]="control.defaultValue" [formControlName]="control.name"/>
</div>
要启用此功能,您需要将 ControlContainer
注入您的 @Component
@Component(
moduleId: `MODULE_ID_HERE`,
selector: "child-textbox-component",
templateUrl: "childTextbox.component.html",
)
export class ChildTextboxComponent
constructor(private controlContainer: ControlContainer, OTHER_PARAMETERS)
【讨论】:
【参考方案3】:为了扩展可能的答案列表,this article by Alexey Zuev 建议在组件装饰器中使用provide:ControlContainer
和useExisting:NgForm
作为将ngForm
指令传递给子组件的一种方式。
@Component(
selector: 'registrant',
templateUrl: 'app/register/registrant.component.html',
**viewProviders: [ provide: ControlContainer, useExisting: NgForm ]**
)
【讨论】:
我们如何动态验证子组件?【参考方案4】:尝试使用嵌套日期选择器时出现同样的问题。上面的解决方案对我不起作用。我通过发出更改日期并直接访问表单控制解决了这个问题。
日历输入.component.ts
@Component(
selector: 'app-calendar-input',
templateUrl: '...',
)
export class CalendarInputComponent
@Output() dateChanged = new EventEmitter<Date|null>();
onClose()
this.dateChanged.emit(this.currentDate);
...
模板
<app-calendar-input
...
(dateChanged)="setFormControlValueByItem(item, col.propName, $event)">
</app-calendar-input>
以及带有表单组和表单控件的组件:
setFormControlValueByItem(item: FormItem<I, FormGroup>, propName: string, value: any): void
item.frameworkFormGroup.controls[propName].setValue(value);
item.frameworkFormGroup.controls[propName].markAsTouched();
item.frameworkFormGroup.controls[propName].markAsDirty();
item.frameworkFormGroup
在我的例子中是 FormGroup 的实例,propName
是我通常会写入属性formControlName
的字段名称。
此解决方案的一个缺点是,在某些情况下,它会降低代码对 FormGroup 和 FormConrol API 中的破坏通道的抵抗力。
另见:https://angular.io/api/forms/FormGroup 和https://angular.io/api/forms/FormControl
【讨论】:
【参考方案5】:只需通过 [formGroup] 绑定在表单块中传递相同或子表单组。
【讨论】:
以上是关于Angular 2 使用嵌套组件创建反应式表单的主要内容,如果未能解决你的问题,请参考以下文章
Angular 9“没有提供ControlContainer的提供者” VSCode错误,带有嵌套的Form Builder表单组