How to Show Split Header in the Material Table Having Nested Group of Data in Angular

How to show split header in the material table having nested group of data in angular

the data source need to be an array of data you want to display. So you have to change

dataSource1 = newData;

to

dataSource1 = newData.data.summary;

You will also need to change the columns in the template since the objects are different, for example:

<td mat-cell *matCellDef="let element" class="similar-cell-width">{{ element.data[0].amount }}</td>

Displaying nested array with mat-table

If it is fixed, always 2 FOO. You can do something like this.

 {{element.FooChain[0].Foo}}

{{element.FooChain[1].Foo}}

However, i'm not sure you can sort and filter by those properties in case you need it.
If the number vary, you have to implement ng-for.

 <mat-cell *matCellDef="let element">
<div>
<ng-container *ngFor="let item of element?.FooChain">
<div >
<span>{{item.Foo}}</span>
</div>
</ng-container>
</div>
</mat-cell>

Trying to use Angular Material table on object that contains another object

The same idea of LotteLemmens using pipe map in your service:

this.httpClient.get(...).pipe(map(data:any[]=>{
return data.map(r=>({
brand: r.brand,
type: r.type,
value: r.value,
fields1: r.rating[0],
fields2: r.rating[1],
fields3: r.rating[2]
}))
}))

Another idea is simply use {{col.indexOf('fields')>0?element[col][+col[6]-1]:element[col]}} (*)

       <ng-container *ngFor="let col of columns" matColumnDef="{{col}}">
<mat-header-cell *matHeaderCellDef>{{ col }}</mat-header-cell>

<mat-cell *matCellDef="let element">
{{col.indexOf('fields')>=0?element[col][+col[6]-1]:element[col]}}
</mat-cell>
</ng-container>

(*) when we use over a string variable[4]get the character in position 5th, we convert in number (adding the + and subtract 1) -usually it's prefer our fields was fields0, fields1 and fields2 to avoid the "substract", but it is our election.

Another aproach is to have a variable columnsWihoutFields

  columnsWihoutFields="brand,type,value"

And make two loops:

      <!--loop over columnsWithoutFields-->
<ng-container *ngFor="let col of columnsWihoutFields" matColumnDef="{{col}}">
<mat-header-cell *matHeaderCellDef>{{ col }}</mat-header-cell>

<mat-cell *matCellDef="let element">
{{ element[col] }}
</mat-cell>
</ng-container>
<!--loops to show the fields-->
<ng-container *ngFor="let col of [1,2,3]" matColumnDef="{{'fields'+col}}">
<mat-header-cell *matHeaderCellDef>{{ 'fields'+col}}</mat-header-cell>
<mat-cell *matCellDef="let element">
{{ element.ratting[col] }}
</mat-cell>
</ng-container>

NOTE: I suppose your columns are "brand,type,value,field1,field2,field3"

Angular Material mat-table Row Grouping

A very simple answer would be to sort by the GroupID, this will put those rows together in groups. However, I'm guessing you want a header row displayed before each group.

You can provide an alternative <mat-row *matRowDef="... that uses a where clause. This can be used to display a non-default set of columns. The where clause takes a function that returns true if that matRowDef should be used.

The data you supply to the table would then be the data rows interspersed with group rows, and the function tells one from the other. Taking Basic use of <table mat-table> as a starter, manually add the groups and add the where clause function to app/table-basic-example.ts:

    import {Component} from '@angular/core';

export interface PeriodicElement {
name: string;
position: number;
weight: number;
symbol: string;
}

export interface Group {
group: string;
}

const ELEMENT_DATA: (PeriodicElement | Group)[] = [
{group: "Group 1"},
{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'},
{group: "Group 2"},
{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'},
{group: "Group 3"},
{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'},
];

/**
* @title Basic use of `<table mat-table>`
*/
@Component({
selector: 'table-basic-example',
styleUrls: ['table-basic-example.css'],
templateUrl: 'table-basic-example.html',
})
export class TableBasicExample {
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
dataSource = ELEMENT_DATA;

isGroup(index, item): boolean{
return item.group;
}
}


/** Copyright 2018 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license */

And add the groupHeader Column and the extra matRowDef to the app/table-basic-example.html:

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

<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->

<!-- Position Column -->
<ng-container matColumnDef="position">
<mat-header-cell *matHeaderCellDef> No. </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.position}} </mat-cell>
</ng-container>

<!-- Name Column -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
</ng-container>

<!-- Weight Column -->
<ng-container matColumnDef="weight">
<mat-header-cell *matHeaderCellDef> Weight </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.weight}} </mat-cell>
</ng-container>

<!-- Symbol Column -->
<ng-container matColumnDef="symbol">
<mat-header-cell *matHeaderCellDef> Symbol </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.symbol}} </mat-cell>
</ng-container>

<ng-container matColumnDef="groupHeader">
<mat-cell *matCellDef="let group">{{group.group}}</mat-cell>
</ng-container>

<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
<mat-row *matRowDef="let row; columns: ['groupHeader']; when: isGroup"> </mat-row>

</mat-table>



<!-- Copyright 2018 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license -->

Here is a finished stackblitz which groups by the element's initial letter.

And here is a far more developed stackblitz just supply the list of columns you want to group by and it will insert the group rows for you. You can also click the group rows to expand or collapse them

And finally here is a Github project that modifies a copy of the MatTableDataSource class from the material codebase. Works nicely with filter and sort, but 'competes' with the paginator as they both limit the view of records in different ways.

How to create a nested mat-table in a table with expandable rows using angular material

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.

angular material mat-table: how to multiply two column values in a 3rd column

just use [(ngModel)]="element.month" in your mat-select

<mat-select placeholder="0" [(ngModel)]="element.month">
<mat-option *ngFor="let month of months" [value]="month.value">
{{ month.viewValue }}
</mat-option>
</mat-select>

And use {{element.price*element.month}}

NOTE: You can also use a getter to get the total and put in the footer of the table

get total()
{
return this.listData.data.map(x=>x.month*x.price).reduce((a,b)=>a+b)
}

See stackblitz



Related Topics



Leave a reply



Submit