FormArray inside Angular Material Table
Solution 1
A little late to the party but I managed to get it working.
https://stackblitz.com/edit/angular-material-table-with-form-59imvq
Component
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>
Solution 2
initTreeFormArray()
does not fire on init the way you need it to. So when the component is built, the html portion is looking for name, when it does not exist.
My .02 is to load a working form and subform groups on init and figure out the second function later. Also, use Mat over html table.
Related videos on Youtube
Marinescu Raluca
Updated on June 04, 2022Comments
-
Marinescu Raluca almost 2 years
Note: I succeded doing FormArray inside classic HTML Table, as seen below . I want to have a FormArray inside Angular Material table and to populate it with data. I tried the same approach as with classic HTML Table, but i was not able to compile it due error "Could not find column with id 'name''"
<div class="form-group"> <form [formGroup]="myForm" role="form"> <div formArrayName="formArrList"> <table> <tr> <th>Name</th> <th>School</th> </tr> <tr *ngFor="let list of myForm.get('formArrList').controls;let i = index" [formGroupName]="i"> <td> <div class="col-sm-6"> <input class="form-control" type="text" formControlName="name"/> </div> </td> <td> <div class="col-sm-6"> <input class="form-control" type="text" formControlName="school"/> </div> </td> </tr> </table> </div> </form>
I try to have a FormArray inside my Angular Material Table Here is my HTML file
<div> <form [formGroup]="myForm" role="form"> <ng-container formArrayName="formArrList"> <mat-table #table [dataSource]="myDataSource"> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> <ng-container *ngFor="let detailsItems of myForm.get('formArrList').controls;let i = index" [formGroupName]="i"> <ng-container matColumnDef="name"> <mat-header-cell *matHeaderCellDef>Name</mat-header-cell> <mat-cell *matCellDef="let element"> <mat-form-field class="" hideRequiredMarker> <input matInput formControlName="name" type="text" class="form-control" autocomplete="off" placeholder="name"> </mat-form-field> </mat-cell> </ng-container> <ng-container matColumnDef="school"> <mat-header-cell *matHeaderCellDef>School</mat-header-cell> <mat-cell *matCellDef="let element"> <mat-form-field class="" hideRequiredMarker> <input matInput formControlName="school" type="text" class="form-control" autocomplete="off" placeholder="school"> </mat-form-field> </mat-cell> </ng-container> </ng-container> </mat-table> </ng-container> </form>
And here is a part of my .TS file
@Component(..) export class DemO implements OnInit { displayedColumns = ['name', 'school']; myForm: FormGroup; formArrList: FormArray; myDataSource: DataSource; dummyData: Element[] = []; ngOnInit(): void { //init form arrayTree this.myForm = this.formBuilder.group({ 'formArrList': new FormArray([]) }); } initTreeFormArray(name: string, school: string) { return this.formBuilder.group({ 'name': [code_any,], 'school': [prio,] }); } renderTableOnButtonClick(){ const control = <FormArray>this.treeForm.controls['formArrList']; control.push(this.initTreeFormArray("DummyName", "DummySchool", element.name)); this.dummyData.push({name: "DummyName", school: "DummySchool"}); this.myDataSource = new sDataSource(this.dummyData); }
-
Marinescu Raluca about 6 yearsanyone with an ideea?
-
-
Marinescu Raluca about 6 yearsI want to use Angular Material Table over classic HTML but i can't use it due error, Could not find column with id 'name''"
-
user7728112 about 6 yearsWhy do you need the matColumnDef="name" at all? formControlName="name" will bind provided its under the form group instance.
-
Mohamad Chami over 4 yearsmatSort does not work, i tried to add sorting on the columns but i failed, do you have a solution for this problem???