简体   繁体   English

Angular 2:在反应形式中使用动态输入

[英]Angular 2: Using dynamic inputs in reactive forms

I have an angular 2 app that heavily uses forms throughout the application. 我有一个角度2应用程序,在整个应用程序中大量使用表单。 Most of the forms are built using the reactive forms module in Angular, but the API I am working against also have lots of "dynamic fields". 大多数表单是使用Angular中的反应表单模块构建的,但我正在使用的API也有很多“动态字段”。

For instance, the "back-end" allows users to create custom fields for certain posts/pages and I want to offer users the ability to use them in my Angular 2 app as well. 例如,“后端”允许用户为某些帖子/页面创建自定义字段,我也希望用户能够在我的Angular 2应用程序中使用它们。

Example

The API gives me a JSON list that looks like this: API为我提供了一个如下所示的JSON列表:

{
    "id": "the-custom-field-id",
    "label": "The input label",
    "min": 4,
    "max": 8,
    "value": "The current value of the custom field"
},
...

Right now, I fetch the list of custom fields in an observable and use ngfor to loop them and produce form elements for each entry, like this: 现在,我在一个observable中获取自定义字段列表,并使用ngfor循环它们并为每个条目生成表单元素,如下所示:

<div *ngFor="let cf of _customFields" class="form-group">

    <label>{{cf.label}}</label>

    <input id="custom-field-{{cf.id}}" type="text" class="form-control" value="{{cf.value}}">

</div>

Then on submit, I reach into the DOM (using jQuery) to get the values using the "IDs" of the custom fields. 然后在提交时,我进入DOM(使用jQuery)以使用自定义字段的“ID”获取值。

This is ugly and goes against the idea of not mixing jQuery and Angular. 这很丑陋,违背了不混合使用jQuery和Angular的想法。

There must be a way of integrating these dynamic forms into Angular instead, so I can use them with control groups and validation rules? 必须有一种方法将这些动态表单集成到Angular中,所以我可以将它们与控制组和验证规则一起使用吗?

Yes, indeed there is. 是的,的确有。 Check out Angular 2's Dynamic Forms . 查看Angular 2的动态表格 The basic jist of it is you create class's(questions) which defined the options for each type of form control you would like to be accessible. 它的基本要点是你创建类(问题),它定义了你想要访问的每种类型的表单控件的选项。 So for instance, as an end result, you could have something like: 例如,作为最终结果,您可能会遇到以下情况:

private newInput;

constructor(){
    // typically you would get your questions from a service/back-end.

    this.newInput = new NumberQuestion({
        key: 'amount',
        label: 'Cash Back',
        value: 21,
        required: true,
        max: 1000,
        min: 10
    });
}

ngOnInit(){
    let control = this.newInput.required ? 
        new FormControl(this.newInput.value, Validators.required)
        : new FormControl(this.newInput.value);

    this.form.addControl(this.newInput.key, control);
}

To create a form that dynamically adds field you need to use FormArray inside the form and add your custom elements there during the runtime. 要创建动态添加字段的表单,您需要在表单内使用FormArray,并在运行时添加自定义元素。 Here's an example of how to dynamically add input fields to allow the user enter more than one email to the form by the click on the button Add Email: https://github.com/Farata/angular2typescript/blob/master/chapter7/form-samples/app/02_growable-items-form.ts 下面是一个如何动态添加输入字段的示例,允许用户通过单击按钮添加电子邮件向表单输入多个电子邮件: https//github.com/Farata/angular2typescript/blob/master/chapter7/form -samples /应用/ 02_growable项,form.ts

See also my example here. 另见我的例子。 Is well commented so easy to understand hope. 好评如此容易理解希望。 https://stackblitz.com/edit/angular-reactive-form-sobsoft https://stackblitz.com/edit/angular-reactive-form-sobsoft

So this is what we need to maintan dynamic fields in app.component.ts 所以这就是我们在app.component.ts中设置动态字段所需要的

ngOnInit () {
  // expan our form, create form array this._fb.array
  this.exampleForm = this._fb.group({
      companyName: ['', [Validators.required,
                         Validators.maxLength(25)]],
      countryName: [''],
      city: [''],
      zipCode: [''],
      street: [''],
      units: this._fb.array([
         this.getUnit()
      ])
    });
 }

     /**
       * Create form unit
       */
      private getUnit() {
        const numberPatern = '^[0-9.,]+$';
        return this._fb.group({
          unitName: ['', Validators.required],
          qty: [1, [Validators.required, Validators.pattern(numberPatern)]],
          unitPrice: ['', [Validators.required, Validators.pattern(numberPatern)]],
          unitTotalPrice: [{value: '', disabled: true}]
        });
      }

      /**
       * Add new unit row into form
       */
      private addUnit() {
        const control = <FormArray>this.exampleForm.controls['units'];
        control.push(this.getUnit());
      }

      /**
       * Remove unit row from form on click delete button
       */
      private removeUnit(i: number) {
        const control = <FormArray>this.exampleForm.controls['units'];
        control.removeAt(i);
      }

Now in HTML: 现在用HTML:

<!-- Page form start -->
  <form [formGroup]="exampleForm" novalidate >

    <div fxLayout="row" fxLayout.xs="column" fxLayoutWrap fxLayoutGap="3.5%" fxLayoutAlign="left" >

      <!-- Comapny name input field -->
      <mat-form-field class="example-full-width" fxFlex="75%"> 
        <input matInput placeholder="Company name" formControlName="companyName" required>
        <!-- input field hint -->
        <mat-hint align="end">
          Can contain only characters. Maximum {{exampleForm.controls.companyName.value.length}}/25
        </mat-hint>
        <!-- input field error -->
        <mat-error *ngIf="exampleForm.controls.companyName.invalid">
          This field is required and maximmum alowed charactes are 25
        </mat-error>
      </mat-form-field>

      <!-- Country input field -->
      <mat-form-field class="example-full-width" > 
        <input matInput placeholder="Country" formControlName="countryName">
        <mat-hint align="end">Your IP country name loaded from freegeoip.net</mat-hint>
      </mat-form-field>

    </div>

    <div fxLayout="row" fxLayout.xs="column" fxLayoutWrap fxLayoutGap="3.5%" fxLayoutAlign="center" layout-margin>

      <!-- Street input field -->
      <mat-form-field class="example-full-width">
        <input matInput placeholder="Street" fxFlex="75%" formControlName="street">
      </mat-form-field>

      <!-- City input field -->
      <mat-form-field class="example-full-width" > 
        <input matInput placeholder="City" formControlName="city">
        <mat-hint align="end">City name loaded from freegeoip.net</mat-hint>
      </mat-form-field>

      <!-- Zip code input field -->
      <mat-form-field class="example-full-width" fxFlex="20%"> 
        <input matInput placeholder="Zip" formControlName="zipCode">
        <mat-hint align="end">Zip loaded from freegeoip.net</mat-hint>
      </mat-form-field>

  </div>
  <br>

  <!-- Start form units array with first row must and dynamically add more -->
  <mat-card formArrayName="units">
    <mat-card-title>Units</mat-card-title>
    <mat-divider></mat-divider>

    <!-- loop throught units -->
    <div *ngFor="let unit of exampleForm.controls.units.controls; let i=index">

      <!-- row divider show for every nex row exclude if first row -->
      <mat-divider *ngIf="exampleForm.controls.units.controls.length > 1 && i > 0" ></mat-divider><br>

      <!-- group name in this case row index -->
      <div [formGroupName]="i">
        <div fxLayout="row" fxLayout.xs="column" fxLayoutWrap fxLayoutGap="3.5%" fxLayoutAlign="center">

          <!-- unit name input field -->
          <mat-form-field  fxFlex="30%"> 
            <input matInput placeholder="Unit name" formControlName="unitName" required>              
          </mat-form-field>

          <!-- unit quantity input field -->
          <mat-form-field  fxFlex="10%"> 
            <input matInput placeholder="Quantity" type="number" formControlName="qty" required>
          </mat-form-field>

          <!-- unit price input field -->
          <mat-form-field  fxFlex="20%"> 
            <input matInput placeholder="Unit price" type="number" formControlName="unitPrice" required>
          </mat-form-field>

          <!-- unit total price input field, calculated and not editable -->
          <mat-form-field > 
            <input matInput placeholder="Total sum" formControlName="unitTotalPrice">
          </mat-form-field>

          <!-- row delete button, hidden if there is just one row -->
          <button mat-mini-fab color="warn" 
                  *ngIf="exampleForm.controls.units.controls.length > 1" (click)="removeUnit(i)">
              <mat-icon>delete forever</mat-icon>
          </button>
        </div>
      </div>
    </div>

    <!-- New unit button -->
    <mat-divider></mat-divider>
    <mat-card-actions>
      <button mat-raised-button (click)="addUnit()">
        <mat-icon>add box</mat-icon>
        Add new unit
      </button>
    </mat-card-actions>
  </mat-card> <!-- End form units array -->    
  </form> <!-- Page form end -->

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM