web-dev-qa-db-fra.com

Table éditable de matériau angulaire à l'aide de FormArray

J'essaie de construire une table modifiable en ligne en utilisant le dernier matériel + cdk pour angular. 

Question

Comment puis-je faire en sorte que mat-table utilise [formGroupName] afin que les champs de formulaire puissent être référencés par le chemin correct de leur formulaire?

C’est ce que j’ai eu jusqu’à présent: Exemple complet de StackBlitz

Modèle

<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>

Composant

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);
  }
}

Problème

Cela ne fonctionnera pas. Rendements de la console 

Erreur ERREUR: Impossible de trouver le contrôle avec le chemin: 'dates -> de'

Il semble que le [formGroupName]="i" n'ait aucun effet, car le chemin devrait être dates -> 0 -> from lors de l'utilisation d'un formArray.

Ma solution actuelle: Pour ce problème, j'ai contourné la recherche de chemin interne (formControlName="from") et utilisé directement le contrôle de formulaire: [formControl]="row.get('from')", mais je voudrais savoir comment je peux Je ne peux pas) utiliser la méthode préférée de la forme réactive. 

Tous les conseils sont les bienvenus. Je vous remercie.


Puisque je pense que c'est un bug, j'ai enregistré un problème avec le dépôt angular/material2 github.

4
Øystein Amundsen

Je voudrais utiliser l'index que nous pouvons obtenir dans matCellDef contraignant:

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

Forked Stackblitz

6
yurzui

voici l exemple de code

En 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>

Code du contrôleur:

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]
    });
  }
1
Rejayi CS