【问题标题】:Angular 4 Dynamic form with nested groups具有嵌套组的Angular 4动态表单
【发布时间】:2017-08-13 07:11:31
【问题描述】:

我想从树形结构中生成一个响应式表单。

这是创建表单项(表单组和控件)的代码。对于嵌套在表单组中的控件,我使用递归模板。

import { Component, Input, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  selector: 'form-item',
  template: `
    <ng-container [formGroup]="form">
      <ng-container *ngTemplateOutlet="formItemTemplate; context:{ $implicit: pathSegments }"></ng-container>

      <ng-template #formItemTemplate let-pathSegments>
        
        <ng-container *ngIf="pathSegments.length > 1">
          <div [formGroupName]="pathSegments[0]" class="form-group">
            <ng-container *ngTemplateOutlet="formItemTemplate; context:{ $implicit: pathSegments.slice(1) }"></ng-container>
          </div>
        </ng-container>

        <ng-container *ngIf="pathSegments.length === 1">
          <div class="form-control">
            <label>{{pathSegments[pathSegments.length - 1]}}</label>
            <!--<input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>-->
            <input type="text"/>
          </div>
        </ng-container>
        
      </ng-template>
    </ng-container>
  `,
})
export class AppControlComponent {

  @Input() form: FormGroup;

  @Input()
  set path(path: string) {
    this.pathSegments = path.split('.');
  }
  pathSegments: string[] = [];

  constructor() { }
}


@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="questionForm">
      <h1>Question Form</h1>

      <form-item [form]="questionForm" path="question"></form-item>
      <form-item [form]="questionForm" path="answers.answer1"></form-item>
      <form-item [form]="questionForm" path="answers.answer2"></form-item>
      <form-item [form]="questionForm" path="answers.answer3"></form-item>

      <button type="submit">submit</button>

      <pre>{{questionForm.value | json}}</pre>
    </form>
  `,
})
export class AppComponent {
  private questionForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.questionForm = this.fb.group({
      question: ['', Validators.required],
      answers: this.fb.group({
        answer1: ['', Validators.required],
        answer2: ['', Validators.required],
        answer3: ['', Validators.required],
      }),
    });
  }
}

@NgModule({
  imports: [BrowserModule, FormsModule, ReactiveFormsModule],
  declarations: [
    AppComponent,
    AppControlComponent
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

表单 HTML 已完美生成。但是当我将formControlName 指令添加到输入时,HTML 生成中断。然后我得到这样的错误:

Error: Cannot find control with name: 'answer1'

【问题讨论】:

  • 你遇到了什么错误?
  • FormControlName 指令使用@Optional() @Host() @SkipSelf() parent: ControlContainer 查找父formGroup
  • 错误:找不到名称为“answer1”的控件
  • @yurzui 这会如何影响我的表单?我认为FormControlName 可以在我的模板中找到parent,不是吗? FormGroupName 也使用它。
  • @yurzui 啊,我明白了。它们应该在同一个模板中,对吧?

标签: forms angular angular-reactive-forms ng-template ng-container


【解决方案1】:

为了支持无限数量的嵌套组,我会使用如下标记:

<ng-container [formGroup]="form">
  <ng-container 
     *ngTemplateOutlet="formItemTemplate; context:{ $implicit: 0, group: form }">
  </ng-container>

  <ng-template #formItemTemplate let-i let-group="group">
      <fieldset *ngIf="i < pathSegments.length - 1" class="form-group">
        <legend>{{ pathSegments[i] }}</legend>
        <ng-container 
          *ngTemplateOutlet="formItemTemplate; 
          context:{ $implicit: i + 1, group: group.get(pathSegments[i]) }">
        </ng-container>
      </fieldset>

      <div *ngIf="i + 1 === pathSegments.length" class="form-control" [formGroup]="group">
        <label>{{pathSegments[pathSegments.length - 1]}}</label>
        <input [formControlName]="pathSegments[pathSegments.length - 1]"/>
      </div>
  </ng-template>
</ng-container>

Plunker Example

【讨论】:

    【解决方案2】:

    正如@yurzui 在评论中提到的那样,出现问题是因为FormControlName 指令使用 @Optional() @Host() @SkipSelf() parent: ControlContainer 找到父 formGroup。

    因此,以下代码可以正常工作,但包含重复代码并支持有限数量的嵌套组。

    @Component({
      selector: 'form-item',
      template: `
        <ng-container [formGroup]="form">
    
          <div class="form-control" *ngIf="pathSegments.length === 1">
            <label>{{pathSegments[pathSegments.length - 1]}}</label>
            <input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
          </div>
    
          <ng-container *ngIf="pathSegments.length === 2" [formGroupName]="pathSegments[0]">
            <div class="form-control">
              <label>{{pathSegments[pathSegments.length - 1]}}</label>
              <input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
            </div>
          </ng-container>
    
          <ng-container *ngIf="pathSegments.length === 3" [formGroupName]="pathSegments[0]">
            <ng-container [formGroupName]="pathSegments[1]">
              <div class="form-control">
                <label>{{pathSegments[pathSegments.length - 1]}}</label>
                <input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
              </div>
            </ng-container>
          </ng-container>
    
          <ng-container *ngIf="pathSegments.length === 4" [formGroupName]="pathSegments[0]">
            <ng-container [formGroupName]="pathSegments[1]">
              <ng-container [formGroupName]="pathSegments[2]">
                <div class="form-control">
                  <label>{{pathSegments[pathSegments.length - 1]}}</label>
                  <input type="text" [formControlName]="pathSegments[pathSegments.length - 1]"/>
                </div>
              </ng-container>
            </ng-container>
          </ng-container>
        </ng-container>
      `,
    })

    有什么改进的方法吗?

    【讨论】:

    • 除了使用 ngSwitch 之外,我不得不做类似的事情
    猜你喜欢
    • 1970-01-01
    • 2019-08-13
    • 2016-10-23
    • 2018-04-22
    • 2014-08-02
    • 1970-01-01
    • 2018-02-21
    • 2018-04-09
    • 1970-01-01
    相关资源
    最近更新 更多