Reputation: 5732
I'm trying to build a table that allows me to perform multiple tasks on it; among those, I'm trying to implement the ability to edit the information of the record right there.
My idea is to create the mat-table as usual, with an additional expandable row to display a small form with the fields I can edit and a button to save the changes and update the table. However I'm not sure on how to actually build this, since I'm not sure on how to build a reactive form for each row in the table; I wouldn't be able to declare it in the components TypeScript file like:
rowForm = new FormGroup({
control1: new FormControl()
})
Because I need it for each record in the table.
<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>
Any ideas on how should I approach this or if there's a more optimal way to do this kind of feature?
Upvotes: 2
Views: 3196
Reputation: 57929
the idea is that your "dataSource" has a form. e.g., you create a function like
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),
})
}
Your data is then
dataSource = ELEMENT_DATA.map(x=>({...x,form:this.createForm(x)}));
In your expandable row you add the form and two buttons "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>
You can see in stackblitz
NOTE: Another option to manage is "edit in place" as in this SO
Update about the note, we can transform the ELEMENT_DATA in a FormArray
dataSource = new FormArray(ELEMENT_DATA.map(x=>this.createForm(x)));
Then we nned an auxiliar function taht return the formGroup at index
getGroup(index:number)
{
return this.dataSource.at(index) as FormGroup
}
The last if use, e.g. getGroup(i).get('position')
to get the control and use as dataSource 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>
And in expandable row:
<form [formGroup]="getGroup(i)">
<input formControlName="name"/>
</form>
Well, the only is that we has no buttons "ok" and "cancel", when chang ethe input you see how the data change automatically
see a new stackblitz
Upvotes: 3