【问题标题】:Angular 2 creating reactive forms with nested componentsAngular 2 使用嵌套组件创建反应式表单
【发布时间】: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 中的新功能寻求帮助呢?

标签: angular typescript angular2-forms


【解决方案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 的反应形式轻松创建和维护。您也可以轻松添加验证。

在回答这个问题之前我点击了这些链接

  1. something similar on stackoverflow

  2. 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:ControlContaineruseExisting: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/FormGrouphttps://angular.io/api/forms/FormControl

    【讨论】:

      【解决方案5】:

      只需通过 [formGroup] 绑定在表单块中传递相同或子表单组。

      【讨论】:

        猜你喜欢
        • 2019-02-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-04
        • 2017-07-21
        • 2018-07-04
        • 2017-08-04
        • 1970-01-01
        • 2018-07-14
        相关资源
        最近更新 更多