【问题标题】:how to get data from API to Angular Mat editable table using Form builder?如何使用表单生成器从 API 获取数据到 Angular Mat 可编辑表?
【发布时间】:2020-07-22 16:30:12
【问题描述】:

这是我尝试使用硬编码数据源并正常工作的方法。但在从 api 获取数据时抛出类似“ERROR TypeError: Cannot read property 'foreach' of undefined”和“ERROR Error: Cannot find control with path: '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>

【问题讨论】:

  • 由于 API 调用是异步的,您需要在订阅中移动动态表单创建。
  • 对不起,我没有得到你。你的意思是表单构建器组应该在订阅中?
  • 移动 this.setUsersForm();内订阅。
  • 是的,我也试过了..但错误仍然存​​在。数据源未定义。

标签: angular angular-material angular-reactive-forms form-control


【解决方案1】:

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

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

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

在 ngOnInit 中,在 subscribe 中创建 FormGroup

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

看看我们如何使用 map 将响应 (res)(对象数组)转换为 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 中,我在自己的组件中返回一个可观察对象,通过您的this.empService.getEmployes 更改this.getEmployes)

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

//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 并管理我们使用类似的表单数组

   <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

【讨论】:

  • 非常感谢。这是一个很好的答案。我喜欢在没有 FormGroup 的情况下使用 FormArray 的方式。但是如果我做 PUT 操作,那么我需要 FormGroup 对吗?。
  • 我将 PUT 操作理解为对唯一元素进行改造的操作,(并且您可以发送到 API,例如 tableFormArray.at(0).value),但您可以拥有接收到的 API一个数组,然后检查每个元素是否存在以在您的数据库中进行更新或插入(在这种情况下是 POST)
  • 是的很酷..再次感谢您的答案和多种方法。
  • @Manmadh,我不确定,我很着急,但我更新了答案并放了一个堆栈闪电,希望对你有所帮助
  • 您的回答对我帮助很大。感谢那。但我们不能使用与 dataSource 相同的 tableFormArray 而不是 rowsSelects,因为保存编辑时可能会出现问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多