Angular: 为什么我的 formArray 没有验证或更新?

Angular: Why does my formArray not validate or update?

我正在尝试获取一组表单字段来验证或更新我的父表单组。 有人可以告诉我我做错了什么吗?

我基本上想要实现的是添加一个具有多个表单域的新 'Vendor Line'。 并验证每个字段。目前只有 formGroup 的外部字段正在验证。我也不想验证每个 formArray 字段。如果有帮助,我正在使用 Ant 的 ng-zorro 框架。 提前致谢。

代码如下:

new-project.component.html

<div nz-row nzGutter="16">
  <div nz-col>
    <h2>Create new Project</h2>
  </div>
</div>
<div nz-row nzGutter="16">
  <div nz-col nzSpan="6" nzOffset="18">
    <button nz-button nzType="primary" nzBlock>
      <i nz-icon nzType="download"></i>Primary
    </button>
  </div>
</div>
<form nz-form nzLayout="vertical" [formGroup]="validateForm" (ngSubmit)="submitForm()">
  <div nz-row nzGutter="16">
    <div nz-col nzSpan="6">
      <nz-form-item>
        <nz-form-label>Project Name</nz-form-label>
        <nz-form-control nzErrorTip="Please enter a project name">
          <input formControlName="projectName" nz-input placeholder="Project Name" />
        </nz-form-control>
      </nz-form-item>
    </div>
    <div nz-col nzSpan="6">
      <nz-form-item>
        <nz-form-label>Company Code</nz-form-label>
        <nz-form-control nzErrorTip="Please select a company code" nzHasFeedback>
          <app-company-select formControlName="selectedCompanyValue"></app-company-select>
        </nz-form-control>
      </nz-form-item>
    </div>
  </div>
  <div nz-row nzGutter="16">
    <div nz-col nzSpan="5">
      Vendor
    </div>
    <div nz-col nzSpan="4">
      Budget
    </div>
    <div nz-col nzSpan="3">
      Available Budget
    </div>
    <div nz-col nzSpan="4">
      Budget to use in Project
    </div>
    <div nz-col nzSpan="2">
      Currency
    </div>
    <div nz-col nzSpan="2">
      Layer 1
    </div>
    <div nz-col nzSpan="2">
      Layer 2
    </div>
    <div nz-col nzSpan="2">
      Layer 3
    </div>
  </div>
  <div formArrayName="projectHeaders">
    <ng-container *ngFor="let projectHeader of validateForm.get('projectHeaders').controls; let i = index" formGroupName="{{i}}">
      <div nz-row nzGutter="16">
        <div nz-col nzSpan="5">
          <nz-form-item>
            <nz-form-control nzErrorTip="Please select a vendor" nzHasFeedback>
              <app-vendor-search formControlName="selectedVendorValue"></app-vendor-search>
            </nz-form-control>
          </nz-form-item>
        </div>
        <div nz-col nzSpan="4">
          <nz-form-item>
            <nz-form-control nzErrorTip="Please select a budget" nzHasFeedback>
              <nz-select formControlName="selectedBudgetValue" nzAllowClear nzPlaceHolder="Budgets">
                <nz-option *ngFor="let o of listOfBudgets" [nzLabel]="o.text" [nzValue]="o.value"> </nz-option>
              </nz-select>
            </nz-form-control>
          </nz-form-item>
        </div>
        <div nz-col nzSpan="3">
          <nz-form-item>
            <nz-form-control>
              <input nz-input placeholder="" formControlName="availableBudget" [disabled]="true" />
            </nz-form-control>
          </nz-form-item>
        </div>
        <div nz-col nzSpan="4">
          <nz-form-item>
            <nz-form-control nzErrorTip="Make sure you have selected a valid budget amount.">
              <nz-input-number formControlName="budgetToUseInProject" [nzFormatter]="parseCurrencyEUR" style="width: 100%;">
              </nz-input-number>
            </nz-form-control>
          </nz-form-item>
        </div>
        <div nz-col nzSpan="2">
          <nz-form-item>
            <nz-form-control>
              <input nz-input placeholder="" formControlName="currency" [disabled]="true" />
            </nz-form-control>
          </nz-form-item>
        </div>
        <div nz-col nzSpan="2">
          <nz-form-item>
            <nz-form-control nzErrorTip="Please select a layer" nzHasFeedback>
              <nz-select formControlName="layer1" nzAllowClear nzPlaceHolder="Layer 1">
                <nz-option *ngFor="let o of layerOneOptions" [nzLabel]="o.text" [nzValue]="o.value"> </nz-option>
              </nz-select>
            </nz-form-control>
          </nz-form-item>
        </div>
        <div nz-col nzSpan="2">
          <nz-form-item>
            <nz-form-control nzErrorTip="Please select a layer" nzHasFeedback>
              <nz-select formControlName="layer2" nzAllowClear nzPlaceHolder="Layer 2">
                <nz-option *ngFor="let o of layerOneTwoOptions" [nzLabel]="o.text" [nzValue]="o.value"> </nz-option>
              </nz-select>
            </nz-form-control>
          </nz-form-item>
        </div>
        <div nz-col nzSpan="2">
          <nz-form-item>
            <nz-form-control nzErrorTip="Please select a layer" nzHasFeedback>
              <nz-select formControlName="layer3" nzAllowClear nzPlaceHolder="Layer 3">
                <nz-option *ngFor="let o of layerThreeOptions" [nzLabel]="o.text" [nzValue]="o.value"> </nz-option>
              </nz-select>
            </nz-form-control>
          </nz-form-item>
        </div>
      </div>
    </ng-container>
  </div>
  <!-- <div formArrayName="projectHeaders">
    <ng-container *ngFor="let projectHeader of validateForm.get('projectHeaders').controls; let i = index">
      <app-vendor-line [group]="projectHeader"></app-vendor-line>
    </ng-container>
  </div> -->
  <div nz-row nzGutter="16">
    <div nz-col nzSpan="24">
      <nz-form-item>
        <nz-form-control nzErrorTip="Please">
          <button nz-button nzSize="large" nzType="primary" nzBlock>Primary</button>
        </nz-form-control>
      </nz-form-item>
    </div>
  </div>
</form>

new-project.component.ts

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

@Component({
  selector: 'app-new-project',
  templateUrl: './new-project.component.html',
  styleUrls: ['./new-project.component.css']
})
export class NewProjectComponent implements OnInit {
  validateForm: FormGroup;

  constructor(private fb: FormBuilder) {

    this.validateForm = this.fb.group({
      selectedCompanyValue: [null, [Validators.required]],
      projectName: [null, [Validators.required]],
      projectHeaders: this.fb.array([]),
      projectLines: []
    });
  }

  ngOnInit() {


    this.addHeaderLine();
  }

  submitForm(): void {
    // tslint:disable-next-line: forin
    for (const i in this.validateForm.controls) {
      this.validateForm.controls[i].markAsDirty();
      this.validateForm.controls[i].updateValueAndValidity();
      if (i === 'projectHeaders') {
        const control = this.validateForm.get('projectHeaders') as FormArray;
        for (const j of control.controls) {
          j.markAsDirty();
          j.updateValueAndValidity();
        }
      }
    }
  }

  initProjectHeader() {
    return this.fb.group({
      selectedVendorValue: [null, Validators.compose([Validators.required])],
      selectedBudgetValue: [null, Validators.compose([Validators.required])],
      availableBudget: [{value: 0, disabled: true }, Validators.compose([Validators.required])],
      budgetToUseInProject: [0, Validators.compose([Validators.required])],
      currency: [{value: 'EUR', disabled: true}, Validators.compose([Validators.required])],
      layer1: [null, Validators.compose([Validators.required])],
      layer2: [null, Validators.compose([Validators.required])],
      layer3: [null, Validators.compose([Validators.required])]
    });
  }

  addHeaderLine() {
    const control = this.validateForm.controls.projectHeaders as FormArray;
    control.push(this.initProjectHeader());
  }

  parseCurrencyEUR(value: number): string {
    return formatNumber(value, 'de_DE');
  }


}

当您使用 Validators.required 时,它将使表单无效或有效,如果 required 失败,该字段也将无效。所以如果它满足条件,它将使表单有效,否则无效。您可以放置​​自定义验证消息或将边框设置为红色并禁用提交按钮以显示该字段是必需的并且不允许提交

在css:: input.form-control.ng-invalid { border: 2px solid red; } 你的 ts 文件很好。如果不满足要求,只需添加验证消息或将字段边框设为红色即可。

我想出了问题所在。显然,您甚至必须将嵌套表单标记为脏并更新它们,所以我修改了 onSubmit 函数来执行此操作。感谢所有其他试图提供帮助的人:)

submitForm(): void {
  // tslint:disable-next-line: forin
  for (const i in this.validateForm.controls) {
    this.validateForm.controls[i].markAsDirty();
    this.validateForm.controls[i].updateValueAndValidity();
    if (i === 'projectHeaders') {
      const control = this.validateForm.get('projectHeaders') as FormArray;
      // tslint:disable-next-line: forin
      for (const j in control.controls) {
        const controlTwo = control.controls[j] as FormGroup;
        // tslint:disable-next-line: forin
        for (const k in controlTwo.controls) {
          controlTwo.controls[k].markAsDirty();
          controlTwo.controls[k].updateValueAndValidity();
        }
      }
    }
    if (i === 'projectLines') {
      const control = this.validateForm.get('projectLines') as FormArray;
      // tslint:disable-next-line: forin
      for (const j in control.controls) {
        const controlTwo = control.controls[j] as FormGroup;
        // tslint:disable-next-line: forin
        for (const k in controlTwo.controls) {
          controlTwo.controls[k].markAsDirty();
          controlTwo.controls[k].updateValueAndValidity();
        }
      }
    }
  }
}