【问题标题】:How to build a dynamic form in a mat-table's expandable row?如何在 mat-table 的可扩展行中构建动态表单?
【发布时间】:2020-11-06 13:40:46
【问题描述】:

我正在尝试构建一个允许我在其上执行多项任务的表;其中,我正在尝试实现在那里编辑记录信息的能力。

我的想法是像往常一样创建 mat-table,另外添加一个 expandable row 以显示一个小表单,其中包含我可以编辑的字段和一个用于保存更改和更新表的按钮。但是我不确定如何实际构建它,因为我不确定如何为表中的每一行构建反应式表单;我无法在组件 TypeScript 文件中声明它,例如:

rowForm = new FormGroup({
            control1: new FormControl() 
         })

因为表中的每条记录都需要它。

 <table mat-table matSort [dataSource]="dataSource" class="table table-borderless admin-users-table" multiTemplateDataRows>
     <ng-container matColumnDef="name">
         <th mat-header-cell *matHeaderCellDef mat-sort-header> NAME </th>
         <td mat-cell *matCellDef="let user">
             <a href="javascript:" [routerLink]="['/setup/users', user._id]">
                 {{ user.name }}
             </a>
         </td>
     </ng-container>

     <ng-container matColumnDef="email">
         <th mat-header-cell *matHeaderCellDef mat-sort-header> EMAIL </th>
         <td mat-cell *matCellDef="let user">
             {{ user.email }}
         </td>
     </ng-container>

     <ng-container matColumnDef="id">
         <th mat-header-cell *matHeaderCellDef> USER ID </th>
         <td mat-cell *matCellDef="let user">
             {{ user._id }}
         </td>
     </ng-container>

     <ng-container matColumnDef="org">
         <th mat-header-cell *matHeaderCellDef mat-sort-header> ORGANIZATION </th>
         <td mat-cell *matCellDef="let user">
             {{ user.organization.name }}
         </td>
     </ng-container>
                
     <!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
     <ng-container matColumnDef="expandedDetail">
         <td mat-cell *matCellDef="let element" class="py-0" [attr.colspan]="columnsToDisplay.length">
             <div class="user-element-detail"
                        [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
                  <div class="user-element-diagram">
                      <div class="user-element-position"> {{element.position}} </div>
                      <div class="user-element-symbol"> {{element.symbol}} </div>
                      <div class="user-element-name"> {{element.name}} </div>
                      <div class="user-element-weight"> {{element.weight}} </div>
                  </div>
                  <div class="user-element-description">
                        {{element.description}}
                     <span class="user-element-description-attribution"> -- Wikipedia </span>
                  </div>
             </div>
         </td>
      </ng-container>


      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let element; columns: displayedColumns;"
                    class="user-element-row"
                    [class.user-expanded-row]="expandedElement === element"
                    (click)="expandedElement = expandedElement === element ? null : element">
      </tr>
      <tr mat-row *matRowDef="let row; columns: ['expandedDetail']"></tr>
</table>

关于我应该如何解决这个问题或者是否有更优化的方法来执行此类功能的任何想法?

【问题讨论】:

    标签: javascript angular typescript angular-material mat-table


    【解决方案1】:

    这个想法是你的“数据源”有一个表格。例如,您创建一个类似

    的函数
    createForm(data:any)
      {
        return new FormGroup({
          name:new FormControl(data.name),
          weight:new FormControl(data.weight),
          symbol:new FormControl(data.symbol),
          position:new FormControl(data.position),
        })
      }
    

    那么你的数据就是

    dataSource = ELEMENT_DATA.map(x=>({...x,form:this.createForm(x)}));
    

    在可展开的行中添加表单和两个按钮“ok”“cancel”

        <form [formGroup]="element.form">
          <input formControlName="name"/>
          <button (click)="element.name=element.form.value.name;
                           expandedElement=null">ok</button>
          <button (click)="element.form.patchValue(element);
                           expandedElement=null">cancel</button>
        </form>
    

    你可以在stackblitz看到

    注意:管理的另一个选项是“就地编辑”,如this SO

    更新关于note,我们可以将ELEMENT_DATA转换成FormArray

    dataSource = new FormArray(ELEMENT_DATA.map(x=>this.createForm(x)));
    

    然后我们添加了一个辅助函数来返回 index 处的 formGroup

      getGroup(index:number)
      {
        return this.dataSource.at(index) as FormGroup
      }
    

    最后一个 if 使用,例如getGroup(i).get('position')获取控件并用作数据源dataSource.controls

    <table mat-table
       [dataSource]="dataSource.controls" multiTemplateDataRows
       class="mat-elevation-z8">
    <ng-container matColumnDef="{{column}}" *ngFor="let column of columnsToDisplay">
        <th mat-header-cell *matHeaderCellDef> {{column}} </th>
        <1--see that we use let i=datIndex becaouse is a multiTemplateDataRows-->
        <td mat-cell *matCellDef="let element;let i=dataIndex"> {{getGroup(i).get(column).value}} </td>
      </ng-container>
    ....
    <table>
    

    在可展开的行中:

    <form [formGroup]="getGroup(i)">
      <input formControlName="name"/>
    </form>
    

    好吧,唯一的原因是我们没有“确定”和“取消”按钮,当更改输入时,您会看到数据如何自动更改

    查看new stackblitz

    【讨论】:

    • 太棒了,绝对不是我想的那样,我可以很容易地创建一个方法来提交表单信息并调用一个服务来更新我的数据库中的数据,对吧?
    • 你在“dataSource”中有数据,在发送之前你需要删除“form”。您可以再次轻松“映射”,例如如果你制作:const dataToSend=this.dataource.map(x=&gt;({name:x.name,position:x.position,weight:x.weigth})),在“dataToSend”中你有一个包含属性名称、位置和重量的对象数组
    • @IvanS95,我在直接使用 FormArray 的答案中添加了另一种方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-13
    • 2017-04-13
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    • 2019-05-01
    • 1970-01-01
    相关资源
    最近更新 更多