如何使用表单生成器从 API 获取数据到 Angular Mat editable table?

how to get data from API to Angular Mat editable table using Form builder?

这是我尝试过的,并且可以很好地处理硬编码数据源。但是从 api 获取数据时抛出错误,如“错误类型错误:无法读取未定义的 属性 'foreach'”和“错误错误:无法找到具有路径的控件:'users -> 0'”。


  ngOnInit() {
    this.empService.getEmployees().subscribe((res: any[]) => {
      this.dataSource = new MatTableDataSource(res);
    });
    this.tableForm = this.formBuilder.group({
      users: this.formBuilder.array([]),
    });
    console.log(this.dataSource);
    this.setUsersForm();
    this.tableForm.get("users").valueChanges.subscribe((users) => {
      console.log("users", users);
    });
  }
  
  private setUsersForm() {
    const userCtrl = this.tableForm.get("users") as FormArray;
    console.log(this.dataSource);
    this.dataSource.foreach((user) => {
      console.log(user);
      userCtrl.push(this.setUsersFormArray(user));
    });
  }

  private setUsersFormArray(user) {
    return this.formBuilder.group({
      employee_name: [user.employee_name],
      location: [user.location],
    });
  }

<form [formGroup]="tableForm">
  <mat-table formArrayName="users" [dataSource]="dataSource">
    <ng-container matColumnDef="EmployeeName">
      <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
      <mat-cell
        *matCellDef="let row let rowIndex = index"
        [formGroupName]="rowIndex"
      >
        <input type="text" size="2" formControlName="employee_name" />
      </mat-cell>
    </ng-container>

    <ng-container matColumnDef="Location">
      <mat-header-cell *matHeaderCellDef>Location </mat-header-cell>
      <mat-cell
        *matCellDef="let row let rowIndex = index"
        [formGroupName]="rowIndex"
      >
        <input type="text" size="7" formControlName="location" />
      </mat-cell>
    </ng-container>

    <!-- Header and Row Declarations -->
    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>
</form>

您不需要有两个单独的数据:dataSource 和 FormArray 并使用 valuesChange。您可以使用 FormArray.controls 作为 mat-table.

的数据源

一如既往地使用 From 数组,创建 FormArray

的 getter
get formArray()
{
    return this.tableForm.get('users') as FormArray
}

在 ngOnInit 中,在订阅中创建 FormGroup

ngOnInit() {
    this.empService.getEmployees().subscribe((res: any[]) => {
      this.tableForm = this.formBuilder.group({
        users: this.formBuilder.array(res.map(x => this.setUsersFormArray(x)))
      });
    });
}

了解我们如何使用映射将响应 (res) - objects- 的数组转换为 FormGroups 数组。

.html 喜欢你的代码 - 否则数据源 -

<form [formGroup]="tableForm">
<mat-table formArrayName="users" [dataSource]="formArray.controls">
  <ng-container matColumnDef="EmployeeName">
    <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
    <mat-cell *matCellDef="let row let rowIndex = index"  
        [formGroupName]="rowIndex"> 
      <input type="text" size="2" formControlName="employee_name"> 
    </mat-cell>
  </ng-container>


  <ng-container matColumnDef="Location">
    <mat-header-cell *matHeaderCellDef>Location  </mat-header-cell>
    <mat-cell *matCellDef="let row let rowIndex = index"  
      [formGroupName]="rowIndex"> 
      <input type="text" size="7" formControlName="location">
    </mat-cell>
  </ng-container>


  <!-- Header and Row Declarations -->
  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
</form>

真的,如果你只是想管理一个Form数组,你不需要创建一个FormGroup,你可以直接使用formArray

tableFormArray:FormArray
ngOnInit() {
    this.empService.getEmployees().subscribe((res: any[]) => {
        this.tableFormArray=this.formBuilder.array(res.map(x => this.setUsersFormArray(x)))
    });
}

<form [formGroup]="tableFormArray">
<mat-table  [dataSource]="tableFormArray.controls">
  <ng-container matColumnDef="EmployeeName">
    <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
    <mat-cell *matCellDef="let row let rowIndex = index"  
        [formGroupName]="rowIndex"> 
      <input type="text" size="2" formControlName="employee_name"> 
    </mat-cell>
  </ng-container>


  <ng-container matColumnDef="Location">
    <mat-header-cell *matHeaderCellDef>Location  </mat-header-cell>
    <mat-cell *matCellDef="let row let rowIndex = index"  
      [formGroupName]="rowIndex"> 
      <input type="text" size="7" formControlName="location">
    </mat-cell>
  </ng-container>


  <!-- Header and Row Declarations -->
  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
</form>

您可以在 this stackblitz 中看到这两个方法(在 stackblitz 中,我 return 在自己的组件中观察到一个,将 this.getEmployes 更改为您的 this.empService.getEmployes)

已更新 如果我们想在 header 中使用 select 过滤列,我们可以采用这种方法(我不确定它是否是最好的方法,也许使用过滤器或过滤选项)。这个想法是,在这种情况下,数据源是一个数字数组,其索引为 formArray 中的“selected”元素。为此

//after create the form array:
this.rowsSelects=this.formArray.controls.map((x,index)=>index)

/* a function to "filter"
   see that we map each formGroup of the formArray in an object with `{index:#,select:true or false}`
  after filter to get only "select", and map to get an array of numbers
*/

selectName(name: string) {
    if (name) {
      this.rowsSelects = this.formArray.controls
        .map((x, index) => ({
          index: index,
          select: x.value.employee_name == name
        }))
        .filter(x => x.select)
        .map(x => x.index);
    } else {
      this.rowsSelects = this.formArray.controls.map((x, index) => index);
    }
  }

作为数据源,我们将使用 rowsSelects 并管理我们使用类似

的 formArray
   <mat-cell *matCellDef="let row let rowIndex = index"  
      [formGroup]="formArray.at(row)">
      <input type="text" size="2" formControlName="employee_name"> 
    </mat-cell>

a quick stackblitz