Friday, May 17, 2024
 Popular · Latest · Hot · Upcoming
182
rated 0 times [  187] [ 5]  / answers: 1 / hits: 47513  / 5 Years ago, thu, september 5, 2019, 12:00:00

I have the below data



[
{
_id: c9d5ab1a,
subdomain: wing,
domain: aircraft,
part_id: c9d5ab1a,
info.mimetype: application/json,
info.dependent: parent,
nested: [
{
domain: aircraft,
_id: c1859902,
info.mimetype: image/jpeg,
info.dependent: c9d5ab1a,
part_id: c1859902,
subdomain: tail
}
]
},
{
_id: 1b0b0a26,
subdomain: fuel,
domain: aircraft,
part_id: 1b0b0a26,
info.mimetype: image/jpeg,
info.dependent: no_parent
}
]


Here if info.dependent: parent then it is nested and if info.dependent: no_parent then it does not have a child. I tried to create a dynamic table but I am stuck on how to make it collapsible/expandable with a nested table.
Here is my code on stackblitz.



<mat-table class= mat-elevation-z8 [dataSource]=dataSource>

<ng-container [matColumnDef]=col *ngFor=let col of displayedColumns>
<mat-header-cell *matHeaderCellDef> {{ col }} </mat-header-cell>
<mat-cell *matCellDef=let element> {{ element[col] }} </mat-cell>
</ng-container>

<mat-header-row *matHeaderRowDef=displayedColumns></mat-header-row>
<mat-row *matRowDef=let row;columns:displayedColumns></mat-row>

</mat-table>


.ts



public data = [
{
_id: c9d5ab1a,
subdomain: wing,
domain: aircraft,
part_id: c9d5ab1a,
info.mimetype: application/json,
info.dependent: parent,
nested: [
{
domain: aircraft,
_id: c1859902,
info.mimetype: image/jpeg,
info.dependent: c9d5ab1a,
part_id: c1859902,
subdomain: tail
}
]
},
{
_id: 1b0b0a26,
subdomain: fuel,
domain: aircraft,
part_id: 1b0b0a26,
info.mimetype: image/jpeg,
info.dependent: no_parent
}
];

dataSource = new MatTableDataSource([]);
displayedColumns = ['_id', 'subdomain', 'domain', 'part_id', 'info.mimetype', 'info.dependent'];

constructor(){
this.displayedColumns = this.displayedColumns;
this.dataSource = new MatTableDataSource(this.data);
}


Required format :-->
nested



The nested format is like below




row 1 --> _id ,subdomain,domain,info.dependent




When we click on that particular row, then it has to expand and display the nested data in a table with the column names and row data.



nested: [
{
domain: aircraft,
_id: c1859902,
info.mimetype: image/jpeg,
info.dependent: c9d5ab1a,
part_id: c1859902,
subdomain: tail
}
]

More From » html

 Answers
14

Note: For those who want to skip the lengthy explanation, here is the StackBlitz example.




What you actually want is to create a nested mat-table where all the nested tables are sortable and can be filtered through as well.



Firstly, since you need to use filtering and sorting in your nested table, you need to create a new MatTableDataSource for it. This can be done initially when you create the main dataSource in the ngOnInit like below.



usersData: User[] = [];

USERS.forEach(user => {
if (user.addresses && Array.isArray(user.addresses) && user.addresses.length) {
this.usersData = [...this.usersData, { ...user, addresses: new MatTableDataSource(user.addresses) }];
} else {
this.usersData = [...this.usersData, user];
}
});
this.dataSource = new MatTableDataSource(this.usersData);


From the expandable rows example in the docs, we can see how to create an expandable row. In the expandable row, we will now have a table along with the Filter input. We will add some conditions so that the row is expandable only if there are addresses present.



<div class=example-element-detail *ngIf=element.addresses?.data.length
[@detailExpand]=element == expandedElement ? 'expanded' : 'collapsed'>
<div class=inner-table mat-elevation-z8 *ngIf=expandedElement>
<mat-form-field>
<input matInput (keyup)=applyFilter($event.target.value) placeholder=Filter>
</mat-form-field>
<table #innerTables mat-table #innerSort=matSort [dataSource]=element.addresses matSort>
<ng-container matColumnDef={{innerColumn}} *ngFor=let innerColumn of innerDisplayedColumns>
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{innerColumn}} </th>
<td mat-cell *matCellDef=let element> {{element[innerColumn]}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef=innerDisplayedColumns></tr>
<tr mat-row *matRowDef=let row; columns: innerDisplayedColumns;></tr>
</table>
</div>
</div>


Now that the row expands only if there are nested elements, we need to get rid of the hover for the users which have no addresses



Here is the CSS responsible for adding a background-color on hover



tr.example-element-row:not(.example-expanded-row):hover {
background: #777;
}


So we just need to add the example-element-row class to our row if the row has an address. If it has no address, the row should not be clickable and there should not be a hover which indicates to the user that the row is in fact not clickable.



<tr mat-row *matRowDef=let element; columns: columnsToDisplay; 
[class.example-element-row]=element.addresses?.data.length
[class.example-expanded-row]=expandedElement === element
(click)=toggleRow(element)>
</tr>


In toggleRow, we will define the logic for what happens when you click a row in the template. We will also implement sort when the user clicks on the row in this function.



@ViewChildren('innerSort') innerSort: QueryList<MatSort>;

toggleRow(element: User) {
element.addresses && (element.addresses as MatTableDataSource<Address>).data.length ? (this.expandedElement = this.expandedElement === element ? null : element) : null;
this.cd.detectChanges();
this.innerTables.forEach((table, index) => (table.dataSource as MatTableDataSource<Address>).sort = this.innerSort.toArray()[index]);
}


Finally, we need to define the applyFilter function so the nested tables can be filtered.



@ViewChildren('innerTables') innerTables: QueryList<MatTable<Address>>;

applyFilter(filterValue: string) {
this.innerTables.forEach((table, index) => (table.dataSource as MatTableDataSource<Address>).filter = filterValue.trim().toLowerCase());
}


Here is a working example on StackBlitz.


[#51687] Thursday, August 29, 2019, 5 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
quentinaveryb

Total Points: 102
Total Questions: 100
Total Answers: 93

Location: Colombia
Member since Mon, May 2, 2022
2 Years ago
quentinaveryb questions
Thu, Aug 6, 20, 00:00, 4 Years ago
Fri, Jul 17, 20, 00:00, 4 Years ago
Mon, Aug 12, 19, 00:00, 5 Years ago
;