Angular2嵌套模板驱动表单

55 angular2-forms angular

这只是疯狂,看起来没有办法让一个表单中的一个输入在子组件中.

我已阅读所有博客和教程以及所有内容,无法解决这个问题.

问题是当一个子组件要有任何形式的指令(ngModel,ngModelGroup或其他......)时,它不会工作.

这只是模板驱动表单中的一个问题

这是plunker:

import { Component } from '@angular/core';

@Component({
  selector: 'child-form-component',
  template: ` 
  <fieldset ngModelGroup="address">
    <div>
      <label>Street:</label>
      <input type="text" name="street" ngModel>
    </div>
    <div>
      <label>Zip:</label>
      <input type="text" name="zip" ngModel>
    </div>
    <div>
      <label>City:</label>
      <input type="text" name="city" ngModel>
    </div>
  </fieldset>`
})

export class childFormComponent{


}

@Component({
  selector: 'form-component',
  directives:[childFormComponent],
  template: `
    <form #form="ngForm" (ngSubmit)="submit(form.value)">
      <fieldset ngModelGroup="name">
        <div>
          <label>Firstname:</label>
          <input type="text" name="firstname" ngModel>
        </div>
        <div>
          <label>Lastname:</label>
          <input type="text" name="lastname" ngModel>
        </div>
      </fieldset>

      <child-form-component></child-form-component>

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

    <pre>
      {{form.value | json}}
    </pre>

    <h4>Submitted</h4>
    <pre>    
      {{value | json }}
    </pre>
  `
})
export class FormComponent {

  value: any;

  submit(form) {
    this.value = form; 
  }
}
Run Code Online (Sandbox Code Playgroud)

yur*_*zui 56

一个简单的解决方案是提供ControlContainerviewProviders像你的孩子组成的数组:

import { ControlContainer, NgForm } from '@angular/forms';

@Component({
 ...,
 viewProviders: [ { provide: ControlContainer, useExisting: NgForm } ]
})
export class ChildComponent {}
Run Code Online (Sandbox Code Playgroud)

Stackblitz示例

另请阅读本文,解释其工作原理.

更新

如果您正在寻找嵌套模型驱动的表单,那么这里是类似的方法:

@Component({
  selector: 'my-form-child',
  template: `<input formControlName="age">`,
  viewProviders: [
    {
      provide: ControlContainer,
      useExisting: FormGroupDirective
    }
  ]
})
export class ChildComponent {
  constructor(private parent: FormGroupDirective) {}

  ngOnInit() {
    this.parent.form.addControl('age', new FormControl('', Validators.required))
  }
}
Run Code Online (Sandbox Code Playgroud)

Ng-run示例

更新2

如果你ControlContainer不确切知道自定义组件的包装类型(例如你的控件在FormArray指令中),那么只需使用通用版本:

import { SkipSelf } from '@angular/core';
import { ControlContainer} from '@angular/forms';

@Component({
 ...,
 viewProviders: [{
   provide: ControlContainer,
   useFactory: (container: ControlContainer) => container,
   deps: [[new SkipSelf(), ControlContainer]],
 }]
})
export class ChildComponent {}
Run Code Online (Sandbox Code Playgroud)

Ng-run示例

  • 妈的 我一直在等这个功能!感谢您的帖子。 (3认同)
  • 这对我不起作用 - 我得到一个“NgForm 没有提供者!”。我怀疑这是因为我们的自定义表单控件有一个 &lt;ng-content&gt; ,然后在其中插入了各种控件。知道解决这个问题的方法是什么吗? (2认同)
  • 如此简单,只需一行... viewProviders:[{provide:ControlContainer,useExisting:NgForm}]`:) (2认同)

Joh*_*lph 22

通过阅读一堆相关的github问题[1] [2],我还没有找到一种直接的方法来将一个孩子Component的控件添加到父级ngForm(有些人也称它们为嵌套形式,嵌套输入或复杂控件) .

所以我将在这里展示的是一种适用于我的解决方法,ngForm为父母和孩子使用单独的指令.它并不完美,但它让我足够接近我停在那里.

childFormComponent用一个ngForm指令声明我(即它不是一个html表单标签,只有指令):

<fieldset ngForm="addressFieldsForm" #addressFieldsForm="ngForm">
  <div class="form-group">
    <label for="email">Email</label>
    <input type="email" class="form-control" [(ngModel)]="model.email" name="email" #email="ngModel" required placeholder="Email">
  </div>
  ...
Run Code Online (Sandbox Code Playgroud)

然后,Component公开addressFieldsForm作为属性,并将自身导出为模板引用变量:

@Component({
  selector: 'mst-address-fields',
  templateUrl: './address-fields.component.html',
  styleUrls: ['./address-fields.component.scss'],
  exportAs: 'mstAddressFields'
})
export class AddressFieldsComponent implements OnInit {
  @ViewChild('addressFieldsForm') public form: NgForm;
  ....
Run Code Online (Sandbox Code Playgroud)

然后,父表单可以使用子表单组件,如下所示:

  <form (ngSubmit)="saveAddress()" #ngFormAddress="ngForm" action="#">
    <fieldset>
      <mst-address-fields [model]="model" #addressFields="mstAddressFields"></mst-address-fields>
      <div class="form-group form-buttons">
        <button class="btn btn-primary" type="submit" [disabled]="!ngFormAddress.valid || !addressFields.form.valid">Save</button>
      </div>
    </fieldset>
  </form>
Run Code Online (Sandbox Code Playgroud)

请注意,提交按钮显式检查表单ngFormAddressaddressFields表单上的有效状态.这样我至少可以合理地组成复杂的形式,即使它有一些样板.

  • 基于你的答案,我找到了一种方法,所以子组件可以自动连接到共享形式serice:https://gist.github.com/jehugaleahsa/c40fb64d8613cfad8f1e1faa4c2a7e33 (5认同)

Art*_*eev 7

另一种可行的解决方法

@Directive({
    selector: '[provide-parent-form]',
    providers: [
        {
            provide: ControlContainer,
            useFactory: function (form: NgForm) {
                return form;
            },
            deps: [NgForm]
        }
    ]
})
export class ProvideParentForm {}
Run Code Online (Sandbox Code Playgroud)

只需将此指令放在节点层次结构顶部的某个子组件中(在任何ngModel之前).

工作原理:NgModel 使用@Host()限定父表单的依赖项查找.因此,父组件中的表单对子组件中的NgModel不可见.但我们可以使用上面演示的代码注入并在子组件中提供它.


Mik*_*kki 2

来自官方文档This directive can only be used as a child of NgForm.

所以我认为你可以尝试将你的子组件包装在不同的组件中ngForm,并期望@Output子组件的父组件结果。如果您需要更多说明,请告诉我。

更新: 这里是Plunker进行了一些更改,我将子表单转换为模型驱动的,因为在提交之前无法侦听表单驱动的表单以进行更新。

  • 您正在使用定义控件的反应式方式,因为整个问题是关于模板驱动的表单。 (3认同)