Angular 2 动态嵌套表单

Posted

技术标签:

【中文标题】Angular 2 动态嵌套表单【英文标题】:Angular 2 Dynamic Nested Form 【发布时间】:2016-10-23 15:19:13 【问题描述】:

基本上我想创建一个带有嵌套对象的动态表单,如下图所示:

收益在模型上的数组中 我们应该能够根据需要添加/删除收益。 表单应同步底层表单控件和模型 支付次数是任意的,应从模型加载到表单中

我没有找到关于如何在 Angular 2 中执行此操作的有效示例,尽管这在 Angular 1 中确实很容易做到。


以下是我最初的问题,我已对其进行了更新以进行澄清(见上文):

首先我只想指出,我知道几天前刚刚发布了新版本的 Angular 2 rc.2。因此,用于创建动态嵌套表单的代码可能已经更改了一些,但没有足够的文档来解决这个问题。

在 Angular 2 的最新版本中(我目前正在使用 rc.1,但计划更新到 rc.2)我需要创建一个这样的表单(视图的伪代码):

<form [ngFormModel]="form" (ngSubmit)="onSubmit()">
  <input type="text" ngControl="name">

  <div *ngFor="let expense for expenses; let i = index;" control-group="expenses">
    <input type="text" ngControl="expense.amount" [(ngModel)]="myModel.expenses[i].amount"> 
    <input type="checkbox" ngControl="expense.final" [(ngModel)]="myModel.expenses[i].final"> 
  </div>

  <a class="button" (click)="addExpenseControl()">Add</a>
  <a class="button" (click)="deleteExpenseControl()">Delete</a>
</form>

所以上面的伪代码不起作用,但老实说,由于缺乏文档,我无法弄清楚如何连接这样的东西。有一些关于嵌套 ControlGroup 的教程,但这不适合这里的情况,因为我们需要能够动态添加和删除控制组,而且我需要能够将它们与模型同步。

我在这里找到了 Angular 团队提供的 plunkr,它允许向表单添加控件——但这不是添加/删除 ControlGroup,而是使用 ControlArray 而我不是确定这是否适用于这里?

我非常熟悉使用较新的基于模型的 Angular 2 表单,但是我正在寻找资源以便正确嵌套它们(动态地!),并将这些嵌套数据绑定到主表单模型中。我将如何引用视图中的嵌套控件?上面的伪代码是否更接近?我会从我的控制器发布代码,但老实说,当涉及到上面的嵌套费用(ControlGroup ??)时,我不知道从哪里开始......

【问题讨论】:

A ControlGroup 与控件完全一样,我相信您可以以相同的方式添加它。 我也是这么想的,但即使是这样,问题仍然存在——我应该为ngControl=".."[(ngModel)]="..." 输入什么代码才能正确连接? 【参考方案1】:

我必须自己解决这个问题,因为 Angular 2 中的表单似乎仍在变化,而且我还没有看到任何其他类似的示例(尽管它似乎是一个非常常见的用例)。

Here is a plunkr 使用 Angular2 RC3 的工作示例。

我正在使用来自this document 的更新的 Angular 2 表单代码。

app.component.ts(包含表单):

import  Component  from '@angular/core';
import REACTIVE_FORM_DIRECTIVES, FormControl, FormGroup, FormArray from '@angular/forms';

@Component(
  selector: 'my-app',
  templateUrl: 'app/app.html',
  directives: [REACTIVE_FORM_DIRECTIVES],
  providers:  []
)
export class AppComponent 
  form: FormGroup;
  myModel:any;

  constructor() 
    // initializing a model for the form to keep in sync with. 
    // usually you'd grab this from a backend API
    this.myModel = 
      name: "Joanna Jedrzejczyk",
      payOffs: [
        amount: 111.11, date: "Jan 1, 2016", final: false,
        amount: 222.22, date: "Jan 2, 2016", final: true
        ]
    

    // initialize form with empty FormArray for payOffs
    this.form = new FormGroup(
      name: new FormControl(''),
      payOffs: new FormArray([])
    );

    // now we manually use the model and push a FormGroup into the form's FormArray for each PayOff
    this.myModel.payOffs.forEach( 
      (po) => 
        this.form.controls.payOffs.push(this.createPayOffFormGroup(po))
    );
  

  createPayOffFormGroup(payOffObj) 
    console.log("payOffObj", payOffObj);
    return new FormGroup(
      amount: new FormControl(payOffObj.amount),
      date: new FormControl(payOffObj.date),
      final: new FormControl(payOffObj.final)
    );
  

  addPayOff(event) 
    event.preventDefault(); // ensure this button doesn't try to submit the form
    var emptyPayOff = amount: null, date: null, final: false;

    // add pay off to both the model and to form controls because I don't think Angular has any way to do this automagically yet
    this.myModel.payOffs.push(emptyPayOff);
    this.form.controls.payOffs.push(this.createPayOffFormGroup(emptyPayOff));
    console.log("Added New Pay Off", this.form.controls.payOffs)
  

  deletePayOff(index:number) 
    // delete payoff from both the model and the FormArray
    this.myModel.payOffs.splice(index, 1);
    this.form.controls.payOffs.removeAt(index);
  

请注意,上面我手动将新的 FormGroup 对象推送到 form.controls.payOffs 数组中,这是一个 FormArray 对象。

app.html(包含表单 html):

  <form (ngSubmit)="onSubmit()" [formGroup]="form">

    <label>Name</label>
    <input type="text" formControlName="name" [(ngModel)]="myModel.name" placeholder="Name">

    <p>Pay Offs</p>
    <table class="simple-table">
      <tr>
        <th>Amount</th>
        <th>Date</th>
        <th>Final?</th>
        <th></th>
      </tr>
    <tbody>
      <tr *ngFor="let po of form.find('payOffs').controls; let i = index">
        <td>
          <input type="text" size=10 [formControl]="po.controls.amount" [(ngModel)]="myModel.payOffs[i].amount">
        </td>
        <td>
          <input type="text" [formControl]="po.controls.date" [(ngModel)]="myModel.payOffs[i].date">
        </td>
        <td>
          <input type="checkbox" [formControl]="po.controls.final" [(ngModel)]="myModel.payOffs[i].final">
        </td>
        <td>
          <button (click)="deletePayOff(i)" style="color: white; background: rgba(255, 0, 0, .5)">x</button>
        </td>
      </tr>
    </tbody>
    <tr>
      <td colspan="4" style="text-align: center; padding: .5em;">
        <button (click)="addPayOff($event)" style="color: white; background: rgba(0, 150, 0, 1)">Add Pay Off</button>
      </td>
    </tr>
    </table>

  </form>

在 html 表单中,我使用如下语句将表单链接到输入上的模型:

... [formControl]="po.controls.amount" [(ngModel)]="myModel.payOffs[i].amount" ...

【讨论】:

我在 html 中使用了find 方法来查找控件,但是在.ts 的情况下,即controler side angular2 无法找到控件并抛出错误cannot read property push of undefined 为什么?这是整个问题***.com/q/39349261/5043867,提前谢谢 @PardeepJain 如果您收到您提到的错误,那么这意味着您尝试调用push 的任何FormArray 在您的代码中的某个时刻都只是undefined。我的猜测是 FormArray 还没有被创建,然后你试图将 FormGroup 推到“无”上。你得到这个工作了吗?你试过我的处理方法吗?我的方法使用 Model Driven 表单,因此我将 Angular 模型与数据以及表单对象保持同步。 对于仍在寻找有关该主题的好教程的人,我找到了这个(scotch.io/tutorials/…)希望这会有所帮助 出于好奇,如果您只想在有收益的情况下添加收益控制,会发生什么?那么动态创建的 payoffs 的 formArray 怎么推? @wolfhoundjesse 此代码应该可以满足您的要求。如果您在构造函数中注意到我创建了一个名为 myModel 的模型,它用于在加载表单时填充表单。您可能会从 Web API 填充您的模型,然后在收到响应后运行相同的代码。加载此表单时,它已经填充了我指定的两条记录,因此它可以按预期工作

以上是关于Angular 2 动态嵌套表单的主要内容,如果未能解决你的问题,请参考以下文章

如何将表单组绑定到 Angular 中嵌套的动态创建的组件

角度动态表单嵌套字段

如何在 AngularJS 表单中嵌套 Angular 2+ 表单?

带有子组件和验证的 Angular 2 嵌套表单

Angular 2 使用嵌套组件创建反应式表单

Angular 2 - 将嵌套的表单构建器标记为已触及