Angular/Reactive Forms - 添加或删除FormControl列表 - 绑定模板(附demo)

Angular/Reactive Forms - Add or Delete an list of FormControl - Binding template (With demo)

我有一个 matSelect 因为我可以 select 一个从 1 到 10 的数字,这取决于我必须用 *ngFor 显示相同数量的 matInput。 select 中默认的数字 select 是 1(所以我们默认也有一个 matInput

<mat-form-field>
  <mat-select placeholder="Number of winners to reward" [value]="selected" (selectionChange)="selectNumber($event)">
    <mat-option *ngFor="let emailNumber of totalEmailsNumber" [value]="emailNumber">
      {{ emailNumber }}
    </mat-option>
  </mat-select>
</mat-form-field>

<div formArrayName="mails">
  <mat-form-field *ngFor="let email of form.get('mails').controls; let i = index">
    <textarea matInput [formControlName]="i"></textarea>
  </mat-form-field>
</div>

添加第一个的默认 FormControl MatInput 我这样做:

this.form.controls['mails'] = this.fb.array(this.emailsNumber.map(() => new FormControl('', Validators.required)));

有效,但我无法管理添加另一个 FormControl 或删除它的逻辑,例如如果用户在 select 中选择 10 然后将其更改为 4, 并管理模板中的绑定。

我也想知道 *ngFor="let email of form.get('mails').controls 是个好习惯吗?


Here's a Minimal StackBlitz Demo to work with

我们可以使用简单的 for 循环,并为 FormArray 使用 pushremoveAt(获取索引)。不要尝试使用 splice,它不会起作用 ;)

你也不想使用 (selectionChange)="selectNumber($event)" 因为那会被触发两次。而是使用 valueChange:

<mat-form-field>
  <mat-select (valueChange)="selectNumber($event)">
    <mat-option *ngFor="let emailNumber of totalEmailsNumber" [value]="emailNumber">
      {{ emailNumber }}
    </mat-option>
  </mat-select>
</mat-form-field>

那么 selectNumber() 可能如下所示:

selectNumber(value) {
  let formArr = this.form.get('mails') as FormArray;
  // user has chosen less fields than already exists
  if (formArr.controls.length > value) {
    const toRemove = formArr.controls.length - value;
    for (let i = 0; i < toRemove; i++ ) {
      // remove last element
      formArr.removeAt(formArr.length - 1);
    }
  } else {
    const addFields = value - formArr.controls.length;
    for (let i = 0; i < addFields; i++) {
      formArr.push(new FormControl(''));
    }
  }
}

StackBlitz

至于最后一个问题...

*ngFor="let email of form.get('mails').controls

非常好!我用那个,或者 getter.

试一试:

import { Component, OnInit } from '@angular/core';
import { FormArray, FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  form: FormGroup;
  totalEmailsNumber: number[];

  constructor(private fb: FormBuilder) {
  }

  ngOnInit() {
    this.totalEmailsNumber = this.createCustomLengthArray(10);
    this.form = this.fb.group({
      mails: this.fb.array([])
    });
  }

  get mails() {
    return (<FormArray>this.form.controls['mails']);
  }

  selectNumber(emailNumbers) {
    const difference = this.mails.length - emailNumbers;
    difference > 0 ? this.removeMails(difference) : this.addMails(difference);
  }

  removeMails(difference) {
    this.createCustomLengthArray(difference)
      .forEach(item => this.mails.removeAt(this.mails.length - 1));
  }

  addMails(difference) {
    this.createCustomLengthArray(difference)
      .forEach(
        item => {
          this.mails.push(this.fb.control(null, Validators.required));
        }
      );
  }

  createCustomLengthArray(length) {
    return (new Array(Math.abs(length)))
      .fill(null)
      .map((item, index) => index + 1);
  }
}

在模板中:

<form [formGroup]="form">
    <mat-form-field>
        <mat-select 
          placeholder="Number of winners to reward" 
          [value]="selected" 
          (selectionChange)="selectNumber($event.value)">
            <mat-option 
              *ngFor="let emailNumber of totalEmailsNumber" 
              [value]="emailNumber">
              {{ emailNumber }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <div formArrayName="mails">
        <mat-form-field 
          *ngFor="let email of form.controls['mails'].controls; let i = index">
            <textarea 
              matInput 
              [formControlName]="i">
      </textarea>
    </mat-form-field>
  </div>
</form>

Here's a Working Sample StackBlitz for your ref.