Angular 2:从多个组件组成表单的惯用法

时间:2016-03-07 22:10:40

标签: forms angular

为了尝试学习Angular 2,我正在制作一个饮食跟踪器,以配合我妻子和我正在尝试的最新时尚饮食。这本书有几个问题可以通过按钮来解答(你有多饿 - 饥饿,非常饥饿等)以及可选的文本输入。还有基于复选框的问题,加上日期。

我正在尝试创建一个由这些多个组件组成的表单 - 一个用于按钮问题,一个用于复选框问题。请参阅此plunker

我很好奇的是将组件(每个组件都有自己的表单元素)组合成Daily形式的惯用方法。每个子组件都有表单元素,也使用*ngFor循环外部文件(question-data.ts)中的数据。目前,每个组件都有一个Daily表单订阅的事件发射器。

以下是每日表单模板(src/daily.component.ts):

  <form>
  <h1>Daily Tracker</h1>
  <br>
  <legend>Date</legend>
  <date-picker (onDataEntered)="dateDataEntered($event)"></date-picker>
  <br>
  <br>
  <button-questions
    *ngFor="#b of buttonQuestions"
    [btn]="b"
    (onDataEntered)="buttonDataEntered($event)">
  </button-questions>
  <checkbox-questions
    *ngFor="#c of checkboxQuestions"
    [cbox]="c"
    (onDataEntered)="checkboxDataEntered($event)">
  </checkbox-questions>
  <br>
  <input type="submit" value="Submit" class="btn btn-primary">
</form>

然后,例如,buttonQuestions就像这样呈现(src/button-questions.component.ts):

<form>
  <h1>Daily Tracker</h1>
  <br>
  <legend>Date</legend>
  <date-picker (onDataEntered)="dateDataEntered($event)"></date-picker>
  <br>
  <br>
  <button-questions
    *ngFor="#b of buttonQuestions"
    [btn]="b"
    (onDataEntered)="buttonDataEntered($event)">
  </button-questions>
  <checkbox-questions
    *ngFor="#c of checkboxQuestions"
    [cbox]="c"
    (onDataEntered)="checkboxDataEntered($event)">
  </checkbox-questions>
  <br>
  <input type="submit" value="Submit" class="btn btn-primary">
</form>

dataEntered()方法执行此操作(单击任何按钮也会调用此方法):

private dataEntered(): void {
  this.btn.inputText = this.inputTextControl.value;
  this.onDataEntered.emit(this.btn);
}

Daily组件然后在其子组件上订阅事件发射器,并将处理最终验证/数据库接口(我仍在尝试学习该部分)。

但是我觉得在子组件上使用原始事件侦听器是错误的方法:例如,我不知道如何将我的子组件上的验证器连接到整个表单上的验证。我也不认为我完全使用内置的Angular 2功能。而且我觉得这不符合松散耦合的原则,正如我所理解的那样。

我的倾向是我应该以某种方式将子组件收集到ControlGroup组件中的主Daily中,但我不知道该怎么做。

谢谢!

1 个答案:

答案 0 :(得分:1)

我不知道这种方法是否首选,但这是一种方法。子组件可以使用ControlContainer中的@angular/forms来访问其父控件的FormGroup

父组件TypeScript文件:

// imports go here

@Component({
  selector: "app-parent",
  templateUrl: "./parent.component.html",
  styleUrls: ["./parent.component.css"]
})
export class ParentComponent implements OnInit {
    // Just create a typical FormGroup.
    fg: FormGroup;

    // Instantiate the FormGroup using your preferred method.
    constructor(fb: FormBuilder) {
        this.fg = this.fb.group({ FirstName: "John", LastName: "Doe" });
    }
}

父组件模板文件:

<!-- Bind a form in the parent component to your FormGroup. -->
<form [formGroup]="fg">
    <!-- Reference the child component.  No need for @Inputs or fancy bindings. -->
    <app-child></app-child>
</form>

子组件TypeScript文件:

import { Component } from "@angular/core";
import { ControlContainer } from "@angular/forms";

@Component({
  selector: "app-child",
  templateUrl: "./child.component.html",
  styleUrls: ["./child.component.css"]
})
export class ChildComponent {
  // Ask the framework for a ControlContainer whose control property has the goods.
  constructor(public controlContainer: ControlContainer) { }

  ngOnInit(): void {
  }
}

子组件模板文件:

<!-- Finally, bind some top-level element to the imported FormGroup. -->
<fieldset [formGroup]="controlContainer.control">
    <legend>Reusable Controls Go Here</legend>

    <div>
        First Name
        <!-- Now, use the FormGroup per normal. -->
        <input formControlName="FirstName" type="text" />
    </div>
    <div>
        Last Name
        <input formControlName="LastName" type="text" />
    </div>
    <div>
        Accessing the FormGroup
        <!-- You can even get fancy. -->
        {{ controlContainer.control.get('FirstName')?.errors | json }}
    </div>
</fieldset>