Angular 7 和表单数组错误找不到带路径的控件

Angular 7 and form arrays error of cannot find a control with path

我正在使用 mat-table 数据源构建一个包含数组的表单组。

我首先创建 table:

<form *ngIf="formGroup" [formGroup]="formGroup">
  <table mat-table [dataSource]="dataSource" *ngIf="total>0" formArrayName="distribution">
    <ng-container matColumnDef="ind_id">
      <th mat-header-cell *matHeaderCellDef> ID </th>
      <td mat-cell *matCellDef="let element">{{element.individual_id}}</td>

    </ng-container>
    <ng-container matColumnDef="ind_name">
      <th mat-header-cell *matHeaderCellDef> Name </th>
      <td mat-cell *matCellDef="let element">{{element.ind_name}}</td>

    </ng-container>
    <ng-container matColumnDef="ind_status">
      <th mat-header-cell *matHeaderCellDef> Ind. Status </th>
      <td mat-cell *matCellDef="let element">{{element.ind_status}}</td>

    </ng-container>
    <ng-container matColumnDef="project_kit">
      <th mat-header-cell *matHeaderCellDef> Kits </th>
      <td mat-cell *matCellDef="let element; let i=index;">
        <div [formGroupName]="i">
          <mat-form-field color="warn" appearance="fill">
            <mat-label>Kits</mat-label>
            <mat-select formControlName="kit" id="kit" placeholder="Kit">
              <mat-option *ngFor="let pk of projectKit" [value]="pk.project_kit">{{pk.kit_name}} part of project
                {{pk.project_name}}</mat-option>
            </mat-select>
          </mat-form-field>&nbsp;
        </div>
      </td>

    </ng-container>
    <ng-container matColumnDef="actual_date">
      <th mat-header-cell *matHeaderCellDef> Actual Date </th>
      <td mat-cell *matCellDef="let element; let i=index;">
        <div [formGroupName]="i">
          <mat-form-field color="warn" appearance="legacy">
            <mat-label>Actual Dist. Date</mat-label>
            <input formControlName="actual_date" matInput [matDatepicker]="picker2" placeholder="Actual Date">
            <mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
            <mat-datepicker #picker2></mat-datepicker>
          </mat-form-field>
        </div>
      </td>
    </ng-container>
    <ng-container matColumnDef="note">
      <th mat-header-cell *matHeaderCellDef> Note </th>
      <td mat-cell *matCellDef="let element;let i=index;">
        <div [formGroupName]="i">
          <mat-form-field color="warn" appearance="legacy">
            <mat-label>Note</mat-label>
            <input formControlName="note" matInput type="text" placeholder="Note">
          </mat-form-field>
        </div>
      </td>
    </ng-container>
    <ng-container matColumnDef="actions">
      <th mat-header-cell *matHeaderCellDef> Actions </th>
      <td mat-cell *matCellDef="let element">

        <button mat-raised-button color="warn" type="submit" color="warn" (click)="addDist(element)">
          <mat-icon>add</mat-icon> Add
        </button>
      </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"
      }"></tr>

  </table>
</form>

对于打字稿:

this.formGroup = this.fb.group({
      distribution: this.fb.array([
    this.createArray()
  ])
});

和 createArray():

createArray(): FormGroup {
    return this.fb.group({
      'note': new FormControl(''),
      'kit': new FormControl(''),
      'actual_date': new FormControl('')
    });

用户上传文件时触发第一个打字稿:

<form [formGroup]="uploadForm" role="form">
      <input #fileInput type="file" formControlName="upload" value="Upload Excel/CSV file"
        (change)="upload($event.target.files)" accept=".xlsx, .xls, .csv" />
      <button mat-raised-button id="inputFile" color="accent" (click)="reset()">
        <mat-icon color="warn">cancel</mat-icon>Reset
      </button>
    </form>

之前上传文件的形式没有报错

但是当我上传文件时,表单数组出现错误:

Error: Cannot find control with path: 'distribution -> 1' at _throwError (forms.js:1790) at setUpFormContainer (forms.js:1772) at FormGroupDirective.push

它指向:

<div [formGroupName]="i">
            <mat-form-field color="warn" appearance="fill">
              <mat-label>Kits</mat-label>
              <mat-select formControlName="kit" id="kit" placeholder="Kit">
                <mat-option *ngFor="let pk of projectKit" [value]="pk.project_kit">{{pk.kit_name}} part of project
                  {{pk.project_name}}</mat-option>
              </mat-select>
            </mat-form-field>&nbsp;
          </div>

编辑

添加 {{formGroup.value| JSON}} 后我得到了这个:

{ "distribution": [ { "note": "", "kit": "", "actual_date": "" } ] }

您必须像这样 formArrayName="distribution"

[formGroup] 开头定义表单数组名称

好的。所以这意味着你没有在数组中添加足够的表单组。您只调用一次创建。所以只有数组中的第一个有效。当索引从 0 变为 1 时,formarray 中没有 formgroup。据我了解,您想为每一行创建表单组。所以你需要多次调用你的函数。

this.formGroup = this.fb.group({
      distribution: this.fb.array([
    this.createArray()
  ])
});

与其每次都创建数组,不如将新的表单组推送到现有的表单数组中。

写一个getter分发方便使用

get distribution() {
   this.formGroup.get('distribution') as FormArray;
}    

那么你可以

this.distribution.push(this.fb.group({
      'note': new FormControl(''),
      'kit': new FormControl(''),
      'actual_date': new FormControl('')
    }));

每一行。我不确定你在哪里创建,但这会为每一行去那里。

并将 formGroup 保留为 i 而不是 i-1 或 0。

问题出在您的数据源上,让 i=index 引用数据源的值,如果数组中的元素少于数据源中的元素,您的代码会崩溃

如果 table 中的所有元素都属于 FormArray,它是 "easy",您可以在 this stackblitz

中查看示例

有两个键,

如何创建表单

myform:FormGroup=new FormGroup({
    distribution:new FormArray(ELEMENT_DATA.map(x=>{
      return new FormGroup({
      position:new FormControl(x.position),
      name:new FormControl(x.name),
      weight:new FormControl(x.weight),
      symbol:new FormControl(x.symbol),

    })}))
  });

以及,我们如何引用控件

<form *ngIf="myform" [formGroup]="myform">
  <ng-container formArrayName="distribution">
<!--see that datasource is myForm.get('distribution').controls-->
<table mat-table [dataSource]="myform.get('distribution').controls" class="mat-elevation-z8" >
<ng-container matColumnDef="position">
    <th mat-header-cell *matHeaderCellDef> No. </th>
    <!--so, "element" is a formGroup-->
    <td mat-cell *matCellDef="let element;let i=index" [formGroup]="element"> <input formControlName="position" > </td>
  </ng-container>
 ....
</table>
</ng-container>
</form>

但是您有一个 "dataSource" 和一个 formArray 一些断开连接。您可以创建一个函数来引用数组

get distributionArray()
{
   return this.myForm.get('distribution') as FormArray
}

并在你的 td 中使用一些像

<td mat-cell *matCellDef="let element;let i=index" 
     [formGroup]="distributionArray.at(i)"> 
    <input formControlName="name" > 
</td>

}

好吧,它不一定对所有人都有价值,但数组中的元素必须与数据源中的元素一样多,例如

this.myform:FormGroup=new FormGroup({
        distribution:new FormArray(ELEMENT_DATA.map(()=>{
          //only two properties empty
          return new FormGroup({
          weight:new FormControl(),
          symbol:new FormControl(),

        })}))
      });

或使用推送

this.myform:FormGroup=new FormGroup({
            distribution:new FormArray([])
});
ELEMENT_DATA.forEach(()=>{
    (this.myForm.get('distribution') as FormArray).push(this.createArray())
}