Reputation: 1771
How would you make rows expandable in angular material tables? One requirement is that I need to be using the angular material table. I would also prefer to use the material accordion to the information provided here.
I want to click on row and show different information for each column. Im looking for something like below. If you click on row 1, rows 2 and 3 appear with different data.
Upvotes: 55
Views: 145648
Reputation: 284
This answer is for only using Angular, we can do the Table row expand and collapse option.
I have given StackBlitz
In the TS file, we have created a variable to store table data.
data = [
{
id: 1,
name: 'Abc',
email: '[email protected]',
isExpand: false,
address: [
{
add1: 'Delhi',
add2: 'Bangalore',
}
]
},
{
id: 2,
name: 'Xyz',
email: '[email protected]',
isExpand: false,
address: [
{
add1: 'Mumbai',
add2: 'Pune',
}
]
},
{
id: 3,
name: 'ijk',
email: '[email protected]',
isExpand: false,
address: [
{
add1: 'Chennai',
add2: 'Bangalore',
}
]
},
{
id: 4,
name: 'def',
email: '[email protected]',
isExpand: false,
address: [
{
add1: 'Kolkata',
add2: 'Hyderabad',
}
]
}
]
In the HTML file, we have a table.
<table>
<thead>
<tr>
<th></th>
<th>SL</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let row of data">
<tr>
<td (click)="row.isExpand = !row.isExpand">
<span *ngIf="!row.isExpand">+</span>
<span *ngIf="row.isExpand">-</span>
</td>
<td>{{ row.id }}</td>
<td>{{ row.name }}</td>
<td>{{ row.email }}</td>
</tr>
<tr *ngIf="row.isExpand">
<td colspan="4">
<table>
<thead>
<th>Address1</th>
<th>Address2</th>
</thead>
<tbody>
<tr *ngFor="let row2 of row.address">
<td>{{ row2.add1 }}</td>
<td>{{ row2.add2 }}</td>
</tr>
</tbody>
</table>
</td>
</tr>
</ng-container>
</tbody>
</table>
Upvotes: 1
Reputation: 8613
As mentioned here by Andrew Seguin this is already feasible out of the box: using the when
predicate.
See this example: https://stackblitz.com/edit/angular-material-expandable-table-rows (thx to Lakston)
Inside of the mat-table
tag you have to use the mat-row
component with a matRipple
directive. When you click on a row the row element will be assigned to the expandedElement
variable:
<mat-row *matRowDef="let row; columns: displayedColumns;"
matRipple
class="element-row"
[class.expanded]="expandedElement == row"
(click)="expandedElement = row">
</mat-row>
But now we have to add our expanded row, that is hidden by default and will be shown if the user clicks on the row above:
<mat-row *matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
[@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'"
style="overflow: hidden">
</mat-row>
Important is here the already mentioned when
predicate. This calls a isExpansionDetailRow
function that is defined in the component itself and checks if the row has a detailRow
property:
isExpansionDetailRow = (row: any) => row.hasOwnProperty('detailRow');
Since RC0 the first param is the index:
isExpansionDetailRow = (i: number, row: any) => row.hasOwnProperty('detailRow');
If you want to have an expanded view for every row, you have to add an "ExpansionDetailRow" identified by the detailRow
property for every row like this:
connect(): Observable<Element[]> {
const rows = [];
data.forEach(element => rows.push(element, { detailRow: true, element }));
return Observable.of(rows);
}
If you would log the rows
variable to the console output, it will look like this:
Mat Table expandable rows (sorting, pagination and filtering)
Upvotes: 116
Reputation: 3351
Here is how you can do it with Angular Material. This example includes pagination and one example with mat-sort-header on the 'name' column. I had to override mat paginator and do a custom sort to make sure the expandable row was still by its parent when it was sorted. This example also allows you to open multiple rows at a time and to close all rows
https://stackblitz.com/edit/angular-material2-issue-zs6rfz
Upvotes: 1
Reputation: 81
It is not possible out of the box, but you can solve it with a little custom code. Take a look at this discussion and this solution (not from me, but the basis for this answer).
In short: Use the material table and add a click-method to the rows:
<md-row *mdRowDef="let row; columns: displayedColumns; let index=index" (click)="expandRow(index, row)" #myRow></md-row>
Add a component for the expanded area. The row_detail.html
contains the html which is in the expanded area.
@Component({
selector: 'app-inline-message',
templateUrl: 'row_detail.html',
styles: [`
:host {
display: block;
padding: 24px;
background: rgba(0,0,0,0.03);
}
`]
})
export class InlineMessageComponent {
@Input() content1: string;
@Input() content2: string;
}
In your component where the table lives you need the method to expand the row. First, add this to your component...
expandedRow: number;
@ViewChildren('myRow', { read: ViewContainerRef }) containers;
... and then add the method:
/**
* Shows the detail view of the row
* @param {number} index
*/
expandRow(index: number, row: DataFromRowFormat) {
if (this.expandedRow != null) {
// clear old message
this.containers.toArray()[this.expandedRow].clear();
}
if (this.expandedRow === index) {
this.expandedRow = null;
} else {
const container = this.containers.toArray()[index];
const factory: ComponentFactory<InlineMessageComponent> = this.resolver.resolveComponentFactory(InlineMessageComponent);
const messageComponent = container.createComponent(factory);
messageComponent.instance.content1= "some text";
messageComponent.instance.content2 = "some more text";
}
}
Upvotes: 7
Reputation: 3117
When CDK was the only way to get something close to a Material Table
, using md-row
's in a regular table was a viable alternative, but since @angular/material 2.0.0-beta.12
ditched CDK
tables and now have their own Data Tables that might fit your needs. See documentation below:
https://material.angular.io/components/table/overview
Upvotes: 2