代码之家  ›  专栏  ›  技术社区  ›  Øystein Amundsen

使用formarray的角度材质可编辑表

  •  6
  • Øystein Amundsen  · 技术社区  · 8 年前

    我正在尝试使用最新的材质+cdk创建一个内联的可编辑表。

    问题

    我怎样才能用垫子桌子 [formGroupName] 以便表单字段可以被其正确的表单路径引用?

    这就是我目前所得到的: Complete StackBlitz example

    模板

    <form [formGroup]="form">
      <h1>Works</h1>
      <div formArrayName="dates" *ngFor="let date of rows.controls; let i = index;">
        <div [formGroupName]="i">
          <input type="date" formControlName="from" placeholder="From date">
          <input type="date" formControlName="to" placeholder="To date">
        </div>
      </div>
    
    
      <h1>Wont work</h1>
      <table mat-table [dataSource]="dataSource" formArrayName="dates">
        <!-- Row definitions -->
        <tr mat-header-row *matHeaderRowDef="displayColumns"></tr>
        <tr mat-row *matRowDef="let row; let i = index; columns: displayColumns;" [formGroupName]="i"></tr>
    
        <!-- Column definitions -->
        <ng-container matColumnDef="from">
          <th mat-header-cell *matHeaderCellDef> From </th>
          <td mat-cell *matCellDef="let row"> 
            <input type="date" formControlName="from" placeholder="From date">
          </td>
        </ng-container>
    
        <ng-container matColumnDef="to">
          <th mat-header-cell *matHeaderCellDef> To </th>
          <td mat-cell *matCellDef="let row">
            <input type="date" formControlName="to" placeholder="To date">
          </td>
        </ng-container>
      </table>
      <button type="button" (click)="addRow()">Add row</button>
    </form>
    

    组成部分

    export class AppComponent implements  OnInit  {
      data: TableData[] = [ { from: new Date(), to: new Date() } ];
      dataSource = new BehaviorSubject<AbstractControl[]>([]);
      displayColumns = ['from', 'to'];
      rows: FormArray = this.fb.array([]);
      form: FormGroup = this.fb.group({ 'dates': this.rows });
    
      constructor(private fb: FormBuilder) { }
    
      ngOnInit() {
        this.data.forEach((d: TableData) => this.addRow(d, false));
        this.updateView();
      }
    
      emptyTable() {
        while (this.rows.length !== 0) {
          this.rows.removeAt(0);
        }
      }
    
      addRow(d?: TableData, noUpdate?: boolean) {
        const row = this.fb.group({
          'from'   : [d && d.from ? d.from : null, []],
          'to'     : [d && d.to   ? d.to   : null, []]
        });
        this.rows.push(row);
        if (!noUpdate) { this.updateView(); }
      }
    
      updateView() {
        this.dataSource.next(this.rows.controls);
      }
    }
    

    问题

    这行不通。控制台产量

    错误错误:找不到路径为“dates->from”的控件

    好像 [formGroupName]="i" 没有效果,因为路径应该是 dates -> 0 -> from 当使用一个礼盒时。

    我目前的解决方法 :对于这个问题,我绕过了内部路径查找( formControlName="from" )直接使用表单控件: [formControl]="row.get('from')" ,但是 我想知道 我如何能(或者至少为什么我不能)使用反应式的首选方式。

    欢迎任何提示。谢谢您。


    因为我觉得这是个错误,所以我注册了 an issue 与Angular/Material2 Github回购协议。

    3 回复  |  直到 8 年前
        1
  •  10
  •   yurzui    8 年前

    我会用我们可以进入的索引 matCellDef 结合:

    *matCellDef="let row; let index = index" [formGroupName]="index"
    

    Forked Stackblitz

        2
  •  7
  •   Rejayi CS    7 年前

    这是示例代码

    在HTML中:

    <form [formGroup]="tableForm">
    
    <mat-table formArrayName="users" [dataSource]="dataSource">
    
      <ng-container cdkColumnDef="position">
        <mat-header-cell *cdkHeaderCellDef> No. </mat-header-cell>
        <mat-cell *cdkCellDef="let row let rowIndex = index"  [formGroupName]="rowIndex"> 
          <input type="text" size="2" formControlName="position"> </mat-cell>
      </ng-container>
    
    
      <ng-container cdkColumnDef="name">
        <mat-header-cell *cdkHeaderCellDef> Name </mat-header-cell>
        <mat-cell *cdkCellDef="let row let rowIndex = index"  [formGroupName]="rowIndex"> 
          <input type="text" size="7" formControlName="name">
        </mat-cell>
      </ng-container>
    
        <ng-container cdkColumnDef="weight">
        <mat-header-cell *cdkHeaderCellDef> Weight </mat-header-cell>
        <mat-cell *cdkCellDef="let row let rowIndex = index"  [formGroupName]="rowIndex"> 
          <input type="text" size="3" formControlName="weight">
        </mat-cell>
      </ng-container>
    
        <ng-container cdkColumnDef="symbol">
        <mat-header-cell *cdkHeaderCellDef> Symbol </mat-header-cell>
        <mat-cell *cdkCellDef="let row let rowIndex = index"  [formGroupName]="rowIndex"> 
          <input type="text" size="2" formControlName="symbol">
        </mat-cell>
      </ng-container>
    
      <!-- Header and Row Declarations -->
      <mat-header-row *cdkHeaderRowDef="displayedColumns"></mat-header-row>
      <mat-row *cdkRowDef="let row; columns: displayedColumns;"></mat-row>
    </mat-table>
    </form>
    

    控制器代码:

    displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
    
    
     dataSource ;
      tableForm: FormGroup;
    
    
    
     constructor(private formBuilder: FormBuilder){
     this.dataSource = [
      {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'},
      {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
      {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
      {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
      {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'},
      {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'},
      {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'},
      {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'},
      {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'},
      {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'},
    ];
      }
    
      ngOnInit(){
        this.tableForm= this.formBuilder.group({
            users: this.formBuilder.array([])
        })
        this.setUsersForm();
        this.tableForm.get('users').valueChanges.subscribe(users => {console.log('users', users)});
      }
      private setUsersForm(){
        const userCtrl = this.tableForm.get('users') as FormArray;
        this.dataSource.forEach((user)=>{
          userCtrl.push(this.setUsersFormArray(user))
        })
      };
      private setUsersFormArray(user){
    
    
        return this.formBuilder.group({
            position:[user.position],
            name:[user.name],
            weight:[user.weight], 
            symbol:[user.symbol]
        });
      }
    
        3
  •  3
  •   Snæbjørn    7 年前

    参加聚会有点晚,但我设法使它在不依赖指数的情况下运转起来。此解决方案还支持从 MatTableDataSource 是的。

    https://stackblitz.com/edit/angular-material-table-with-form-59imvq

    组成部分

    import {
      Component, ElementRef, OnInit
    } from '@angular/core';
    import { Observable } from 'rxjs';
    import { map } from 'rxjs/operators'
    import { AlbumService } from './album.service';
    import { UserService } from './user.service';
    import { Album } from './album.model';
    import { User } from './user.model';
    import { FormArray, FormGroup, FormBuilder } from '@angular/forms';
    import { MatTableDataSource } from '@angular/material';
    
    @Component({
      selector: 'table-form-app',
      templateUrl: 'app.component.html'
    })
    export class AppComponent implements OnInit {
      form: FormGroup;
      users: User[] = [];
      dataSource: MatTableDataSource<any>;
      displayedColumns = ['id', 'userId', 'title']
      constructor(
        private _albumService: AlbumService,
        private _userService: UserService,
        private _formBuilder: FormBuilder
        ) {}
    
      ngOnInit() {
        this.form = this._formBuilder.group({
          albums: this._formBuilder.array([])
        });
        this._albumService.getAllAsFormArray().subscribe(albums => {
          this.form.setControl('albums', albums);
          this.dataSource = new MatTableDataSource((this.form.get('albums') as FormArray).controls);
          this.dataSource.filterPredicate = (data: FormGroup, filter: string) => { 
              return Object.values(data.controls).some(x => x.value == filter); 
            };
        });
        this._userService.getAll().subscribe(users => {
          this.users = users;
        })
      }
    
      get albums(): FormArray {
        return this.form.get('albums') as FormArray;
      }
    
      // On user change I clear the title of that album 
      onUserChange(event, album: FormGroup) {
        const title = album.get('title');
    
        title.setValue(null);
        title.markAsUntouched();
        // Notice the ngIf at the title cell definition. The user with id 3 can't set the title of the albums
      }
    
      applyFilter(filterValue: string) {
        this.dataSource.filter = filterValue.trim().toLowerCase();
      }
    }
    

    HTML格式

    <mat-form-field>
      <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
    </mat-form-field>
    
    <form [formGroup]="form" autocomplete="off">
        <mat-table [dataSource]="dataSource">
    
          <!--- Note that these columns can be defined in any order.
                The actual rendered columns are set as a property on the row definition" -->
    
          <!-- Id Column -->
          <ng-container matColumnDef="id">
            <mat-header-cell *matHeaderCellDef> Id </mat-header-cell>
            <mat-cell *matCellDef="let element"> {{element.get('id').value}}. </mat-cell>
          </ng-container>
    
          <!-- User Column -->
          <ng-container matColumnDef="userId">
            <mat-header-cell *matHeaderCellDef> User </mat-header-cell>
            <mat-cell *matCellDef="let element" [formGroup]="element">
              <mat-form-field floatLabel="never">
                <mat-select formControlName="userId" (selectionChange)="onUserChange($event, element)" required>
                  <mat-option *ngFor="let user of users" [value]="user.id">
                    {{ user.username }}
                  </mat-option>
                </mat-select>
              </mat-form-field>
            </mat-cell>
          </ng-container>
    
          <!-- Title Column -->
          <ng-container matColumnDef="title">
            <mat-header-cell *matHeaderCellDef> Title </mat-header-cell>
            <mat-cell *matCellDef="let element;" [formGroup]="element">
              <mat-form-field floatLabel="never" *ngIf="element.get('userId').value !== 3">
                <input matInput placeholder="Title" formControlName="title" required>
              </mat-form-field>
            </mat-cell>
          </ng-container>
    
          <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
          <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
        </mat-table>
    </form>
    <mat-accordion>
      <mat-expansion-panel>
        <mat-expansion-panel-header>
          <mat-panel-title>
            Form value
          </mat-panel-title>
        </mat-expansion-panel-header>
        <code>
          {{form.value | json}}
        </code>
      </mat-expansion-panel>
    </mat-accordion>