【问题标题】:angular 2 model driven nested form componentsangular 2 模型驱动的嵌套表单组件
【发布时间】:2017-01-30 11:53:19
【问题描述】:

我有什么:

我正在构建一个 ionic 2 应用程序并构建了一个基本的 angular 2 组件,其中包含

  • 输入字段

  • 显示输入标题的标签

  • 显示任何验证错误的标签

我将把它称为我的输入组件

我有一个带有表单的页面组件,并且当前有文本输入。 1 个常规输入(密码)和 1 个包含在我的输入组件(用户名)中的输入。

这是我的页面组件的相关部分

ngOnInit() {
  this.loginForm = this.formBuilder.group({
    username: ['', Validators.required],
    password: ['', Validators.required]
  });
}

这是页面组件模板

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

  <!-- My input component -->
  <aw-input-text id="username" name="Username" [formInput]="loginForm.controls.username"></aw-input-text>

  <!-- A standard input control -->
  <ion-item [class.error]="loginForm.controls.password.errors">
    <ion-label floating>Password</ion-label>
    <ion-input type="text" value="" name="password" formControlName="password"></ion-input>
    <p *ngIf="loginForm.controls.password.errors">This field is required!</p>
  </ion-item>

  <button type="submit" class="custom-button" [disabled]="!loginForm.valid" block>Login</button>

</form>

这是我的输入组件的模板

<!-- Component template -->
<form [formGroup]="formGroup">
    <ion-item>
        <ion-label floating>{{inputName}}</ion-label>
        <ion-input type="text" formControlName="inputValue"></ion-input>
        <p *ngIf="!formGroup.controls.inputValue.valid">This field is required!</p>
    </ion-item>
</form>

这是输入组件

import {Component, Input} from '@angular/core';
import {FormBuilder} from '@angular/forms';

@Component({
  selector: 'aw-input-text',
  templateUrl: 'build/shared/aw-input-text/aw-input-text.html'
})
export class AwInputText {

  @Input('formInput')
  public formInput;
  @Input('name')
  public inputName;
  public formGroup;
  constructor(private formBuilder: FormBuilder) {
  }

  ngOnInit() {
     this.formGroup = this.formBuilder.group({
        inputValue: this.formInput
     });
  }

}

组件正确渲染。

问题:

组件内的输入不会更新其所在表单的有效状态。

当我填写用户名时,密码表格生效

当我填写密码时,表单中的用户名仍然无效

所以表单可以看到输入组件的有效状态,只是输入组件改变有效状态不会触发表单更新。

可能的解决方案 1

如本文所述和 plunk

https://scotch.io/tutorials/how-to-build-nested-model-driven-forms-in-angular-2

https://plnkr.co/edit/clTbNP7MHBbBbrUp20vr?p=preview

我可以修改我的页面组件以创建一个表单组,其中包含一个嵌套表单组,用于我想在输入组件中使用的每个表单控件

ngOnInit() {
  this.loginForm = this.formBuilder.group({
    username: this.formBuilder.group({
      username: ['', Validators.required],
    }),
    password: ['', Validators.required],
  });
}

这个解决方案适合文章中他们添加输入控件数组的场景,但在我的情况下,我认为这感觉很hacky

可能的解决方案 2

我考虑过的另一个 hacky 解决方案是使用输入组件中的 @output 指令来触发页面组件上的事件,该事件会在输入组件更新时刷新表单。

更新输入组件

this.formGroup.controls.inputValue.valueChanges.subscribe(value => {
  this.formUpdated.emit({
    value: value
  })
});

更新页面组件

public onUpdated(value){
  this.loginForm.updateValueAndValidity();
}

并更新页面组件模板

<aw-input-text id="username" name="Username" (updated)="onUpdated($event)" [formInput]="loginForm.controls.username"></aw-input-text>

这确实为我提供了所需的功能,但我认为在每个表单页面上都有一个事件处理程序以使输入组件正常工作似乎也有点笨拙。

问题

有没有办法让我的组件更新它所在表单的有效状态(请记住,我想在每个表单中多次重复使用此组件)而不求助于上述解决方案.

【问题讨论】:

    标签: javascript forms angular angular2-forms angular2-components


    【解决方案1】:

    您可以将@Input 您的loginForm 放入嵌套组件中,例如parentForm。然后在子组件init上注册嵌套的formGroupparentForm,在子组件destroy上注销。

    【讨论】:

    • 如果其他人需要有关此方法的更多详细信息,我按照this blog post 做了类似的事情
    【解决方案2】:

    我在我的案例中所做的(嵌套动态形式也是如此)在某种程度上类似于 Marcin 的响应。

    我将现有的FormGroup 传递为parentForm,而我的Component 的视图如下所示:

    <fieldset [formGroup]="parentForm">
        <label *ngIf="label" [attr.for]="key">{{label}}</label>
        <input [id]="key" [type]="type" [formControlName]="key" />
    </fieldset>
    

    它适合我的需要。希望对你也有帮助。

    更新: 我创建了一个库来加速动态表单的创建。您可以从这个答案中了解我是如何使用该技术的:https://www.npmjs.com/package/dorf

    【讨论】:

      【解决方案3】:

      谢谢大家的回答。最后我们创建了两个组件,一个自定义表单组件和一个自定义输入组件。

      我们在自定义表单组件中嵌套任意数量的自定义输入组件,自定义表单组件使用@ContentChildren 来识别和注册所有子自定义输入组件。

      这样我们就不必将表单传递到每个输入中,并且每个输入都不会有一堆嵌套的表单组。

      // Each CustomInputText component exposes a FormControl and 
      // a control definition which has additional info about the control
      @ContentChildren(CustomInputText, {descendants: true})
      public customInputComponents: QueryList<CustomInputText>;
      
      private initialised;
      
      public ngAfterContentChecked() {
        // Only initialise the form group once
        if (!this.initialised) {
          this.initialised = true;
          this.customInputComponents.forEach((input)=>{
              this.formGroup.addControl(input.controlDefinition.id, input.formControl); 
          });
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-01-30
        • 2019-04-08
        • 2018-04-09
        • 2018-02-21
        • 1970-01-01
        • 2017-06-27
        • 2016-10-23
        相关资源
        最近更新 更多