I've a dynamic mat-tab that have a nested object on a mat-table within each tab. I want to implement a filter on each mat-table within the tab. How do I do that? The datasource for the mat-table is within the tab nested object.
Here is the HTML :
<div class="container-inside">
<mat-card>
<mat-card-header>
<mat-card-title><strong>Warehouse Inventory</strong></mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-tab-group>
<mat-tab *ngFor="let wh of whouses; let i = index" [label]="wh.description">
<div class="inv-content">
<div> </div>
<div>
<button class="float-right" mat-icon-button (click)="addInventory({loc_code: wh.loc_code, desc: wh.description})"><mat-icon>add</mat-icon></button><button class="float-right" mat-icon-button (click)="refreshData()"><mat-icon>refresh</mat-icon></button>
</div>
<div> </div>
<mat-form-field appearance="fill" class="input-filter">
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Ex. ium" #input>
</mat-form-field>
<div>
<table mat-table [dataSource]="wh.inv" class="mat-elevation-z8">
<ng-container matColumnDef="category">
<th mat-header-cell *matHeaderCellDef> Category</th>
<td mat-cell *matCellDef="let element"> {{element.category}} </td>
</ng-container>
<ng-container matColumnDef="loc_code">
<th mat-header-cell *matHeaderCellDef> WH Code</th>
<td mat-cell *matCellDef="let element"> {{element.loc_code}} </td>
</ng-container>
<ng-container matColumnDef="project_no">
<th mat-header-cell *matHeaderCellDef> Project No.</th>
<td mat-cell *matCellDef="let element"> {{element.project_no}} </td>
</ng-container>
<ng-container matColumnDef="item_code">
<th mat-header-cell *matHeaderCellDef> Item Code </th>
<td mat-cell *matCellDef="let element"> {{element.item_code}} </td>
</ng-container>
<ng-container matColumnDef="item">
<th mat-header-cell *matHeaderCellDef> Item </th>
<td mat-cell *matCellDef="let element"> {{element.item}} </td>
</ng-container>
<ng-container matColumnDef="qty">
<th mat-header-cell *matHeaderCellDef> Quantity </th>
<td mat-cell *matCellDef="let element"> {{element.qty}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</div>
</mat-tab>
</mat-tab-group>
</mat-card-content>
</mat-card>
</div>
I want to be able to add a tooltip that displays when hovering over the header of a column in a mat-table.
Is there a way to do this, because it does not display when I add the mat tooltip indicator.
<table mat-table [dataSource]="marketDataSource" matSort multiTemplateDataRows class="w-100">
<!-- Status Status -->
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header matTooltip="Info about the action">
Status
</th>
<td mat-cell *matCellDef="let element">
<span>
<mat-slide-toggle color="primary"></mat-slide-toggle>
</span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="marketDisplayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let element; columns: marketDisplayedColumns;">
</table>
import MatTooltip module on your component.ts
import { MatTooltipModule } from '#angular/material/tooltip';
I am writing unit test cases for Angular material table. Here's the template code snippet:
<table mat-table [dataSource]="dataSource" matSort>
<!-- Edit/Delete -->
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef>
<div class="mat-table-header">Action</div>
</th>
<td mat-cell *matCellDef="let type">
<div class="row-action">
<button class="btn--delete" mat-icon-button aria-label="delete button" (click)="openDeleteDialog(type)">
<span class="icon-delete"></span>
</button>
<button class="btn--edit" mat-icon-button aria-label="edit button"
(click)="openAddOrEditDialog(category.TYPE, type)">
<span class="icon-edit"></span>
</button>
</div>
</td>
</ng-container>
<!-- Type Name -->
<ng-container matColumnDef="avtName">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="mat-table-header">Type</th>
<td mat-cell *matCellDef="let type">{{ type.avtName }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let type; columns: displayedColumns"></tr>
</table>
I am testing the openAddOrEditDialog which opens a MatDialog. I have inserted 2 records into the data table. When I try to test the clicking of the edit button by using fixture.debugElement.query(By.css(".btn--edit")), I always get null, meaning the records didn't get rendered in the data table, but, when I query the table using fixture.debugElement.query(By.css("table")), I can see the records in the console.log(fixture.debugElement.query(By.css("table"))).
Where am I going wrong?
Here's my test case:
it(`some description`, () => {
const spy = jest.spyOn(notificationService, "notifySuccess");
const editBtn: HTMLElement = fixture.debugElement.query(By.css(".btn--edit"))
.nativeElement;
editBtn.click();
expect(spy).toBeCalledWith("Type was successfully updated");
});
I got the answer.
We can not use fixture.debugElement.query for Angular Material Data Table, instead it works with fixture.debugElement.nativeElement.querySelector.
It is suggested in https://medium.com/#penghuili/angular-unit-test-cant-query-material-table-e2c25abc4937
My Data Source is like below
[{"isGroup":true,"groupName":"MV Reddy","items":[{"id":1,"name":"MV Reddy","verticalid":5,"vertical":"Colocation - Large > 20 Racks","target":"150","sap":"80","colo":"20","others":"50","quarter":1,"year":2019},{"id":10,"name":"MV Reddy","verticalid":6,"vertical":"Govt","target":"150","sap":"80","colo":"20","others":"50","quarter":1,"year":2020}]},{"isGroup":true,"groupName":"Neeraj Jha","items":[{"id":2,"name":"Neeraj Jha","verticalid":4,"vertical":"Alliances","target":"70","sap":"20","colo":"30","others":"20","quarter":2,"year":2019},{"id":5,"name":"Neeraj Jha","verticalid":4,"vertical":"Alliances","target":"150","sap":"80","colo":"20","others":"50","quarter":1,"year":2019}]},{"isGroup":false,"groupName":"Suresh Rathod","items":[{"id":3,"name":"Suresh Rathod","verticalid":3,"vertical":"C4C India (Public Cloud)","target":"100","sap":"20","colo":"30","others":"50","quarter":1,"year":2019}]},{"isGroup":false,"groupName":"Arun Dubey","items":[{"id":4,"name":"Arun Dubey","verticalid":6,"vertical":"Govt","target":"150","sap":"80","colo":"20","others":"50","quarter":4,"year":2019}]},{"isGroup":true,"groupName":"Atin Singh","items":[{"id":6,"name":"Atin Singh","verticalid":5,"vertical":"Colocation - Large > 20 Racks","target":"150","sap":"80","colo":"20","others":"50","quarter":1,"year":2020},{"id":7,"name":"Atin Singh","verticalid":2,"vertical":"IAAS and Rest of Ctrls Services","target":"150","sap":"80","colo":"20","others":"50","quarter":1,"year":2020},{"id":8,"name":"Atin Singh","verticalid":3,"vertical":"C4C India (Public Cloud)","target":"150","sap":"80","colo":"20","others":"50","quarter":1,"year":2020},{"id":9,"name":"Atin Singh","verticalid":6,"vertical":"Govt","target":"150","sap":"80","colo":"20","others":"50","quarter":1,"year":2020}]}]
and I wrote/printing my Table like this:
<table class="mat-elevation-z8 " mat-table matSort [dataSource]='targetData' (matSortChange)="sortData($event)">
<ng-container matColumnDef="Employee">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Employee </th>
<td mat-cell *matCellDef="let element"> {{element.items[0].name}}
</td>
</ng-container>
<ng-container matColumnDef="groupData">
<ng-container *matCellDef="let group">
<ng-container *ngFor="let groupCol of group.items;index as i;">
<ng-container *ngIf="i==0 else newRow;">
<td mat-cell rowspan="group.items.length">
{{group.groupName}}
</td>
<td mat-cell>
{{groupCol.vertical}}
</td>
<td mat-cell>
{{groupCol.target}}
</td>
<td mat-cell>
{{groupCol.sap}}
</td>
<td mat-cell>
{{groupCol.colo}}
</td>
<td mat-cell>
{{groupCol.others}}
</td>
</ng-container>
<ng-template #newRow>
<tr mat-row>
<td mat-cell>
{{groupCol.vertical}}
</td>
<td mat-cell>
{{groupCol.target}}
</td>
<td mat-cell>
{{groupCol.sap}}
</td>
<td mat-cell>
{{groupCol.colo}}
</td>
<td mat-cell>
{{groupCol.others}}
</td>
</tr>
</ng-template>
</ng-container>
</ng-container>
</ng-container>
<ng-container matColumnDef="Vertical">
<th mat-header-cell *matHeaderCellDef>Vertical</th>
<td mat-cell *matCellDef="let element"> {{element.items[0].vertical}} </td>
</ng-container>
<ng-container matColumnDef="Target">
<th mat-header-cell *matHeaderCellDef>Target</th>
<td mat-cell *matCellDef="let element"> {{element.items[0].target}} </td>
</ng-container>
<ng-container matColumnDef="SAP">
<th mat-header-cell *matHeaderCellDef>SAP</th>
<td mat-cell *matCellDef="let element">{{element.items[0].sap}}</td>
</ng-container>
<ng-container matColumnDef="COLO">
<th mat-header-cell *matHeaderCellDef>COLO</th>
<td mat-cell *matCellDef="let element">{{element.items[0].colo}}</td>
</ng-container>
<ng-container matColumnDef="Others">
<th mat-header-cell *matHeaderCellDef>Others</th>
<td mat-cell *matCellDef="let element">{{element.items[0].others}}</td>
</ng-container>
<ng-container matColumnDef="Action">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Action </th>
<td mat-cell *matCellDef="let element">
<div class="tableActions ">
<button class="view mat-button" (click)='edit(element.items[0])'>
<i class="material-icons">
create
</i>
</button>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="TargetTable;"></tr>
<tr mat-row *matRowDef="let row; columns: TargetTable;"></tr>
<tr mat-row *matRowDef="let row; columns: ['groupData']; when: isAGroup"></tr>
</table>
I am unable to print another row inside a table while looping through my data
Final result should be like below.
Your approach goes in the right direction as you're using the rowspan attribute. The template however is unnecessary complex and can be simplified if you pre-process your data.
I solved a similar problem in the open source project Koia using a RowSpanComputer class. Within summary-table.component.html, the computed row spans are then used to define the rowspan attribute of the td element.
[attr.rowspan]="rowSpans[iCol][iRow].span"
The RowSpanComputer class computes the rowspan for each cell out of the specified table data (array of rows). It basically loops over the rows and increments the rowspan for cells as long as their value remains unchanged and left located cells were also spanned. As soon as the value changes, the corresponding rowspan is reset to zero.
Please have a look at the following StackBlitz that uses the data you provided. This must obviously further be refined in order to obtain the result you expect.
UPDATE
If you want to have rowspan applied for cells even if left located cells were not spanned, simply remove the following line from the RowSpanComputer class.
spanColumnContexts.slice(iCol + 1).forEach(c => c.spannedRow = {});
I did this in this StackBlitz where a renamed RowSpanComputer to GroupingRowSpanComputer in order to avoid confusion.
My Table:
<table mat-table matSort [dataSource]="targetData" class="mat-elevation-z8">
<ng-container matColumnDef="Employee">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Employee </th>
<td mat-cell *matCellDef="let element" [attr.rowspan]="rowSpanLength(element)"
[style.display]="rowSpanLength(element)>0 ? '' : 'none'">
{{element.name}}
</td>
</ng-container>
<ng-container matColumnDef="Vertical">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Vertical </th>
<td mat-cell *matCellDef="let element"> {{element.vertical}}
</td>
</ng-container>
<ng-container matColumnDef="Target">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Target </th>
<td mat-cell *matCellDef="let element"> {{element.target}}
</td>
</ng-container>
<ng-container matColumnDef="SAP">
<th mat-header-cell *matHeaderCellDef mat-sort-header> SAP </th>
<td mat-cell *matCellDef="let element"> {{element.sap}}
</td>
</ng-container>
<ng-container matColumnDef="COLO">
<th mat-header-cell *matHeaderCellDef mat-sort-header> COLO </th>
<td mat-cell *matCellDef="let element"> {{element.colo}}
</td>
</ng-container>
<ng-container matColumnDef="Others">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Others</th>
<td mat-cell *matCellDef="let element"> {{element.others}}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="TargetTableColumns"></tr>
<tr mat-row *matRowDef="let row; columns: TargetTableColumns"></tr>
</table>
And my rowSpanLength function like below:
rowSpanLength(item) {
let nameArr = this.targetData.filter(i => i.name === item.name);
//return length if the item is first element in above array else 0
return nameArr[0].id === item.id ? nameArr.length : 0;
}
This function will keep rowSpan if the item/tr is first one from Group, for rest of all items the Name td will be hidden.
The result is like below which is what i wanted. got this idea from #uminder (https://stackoverflow.com/users/2358409/uminder) stackblitz
Not able to implement Sorting and Pagination in Angular 7, in mat-accordions (mat-expansion-panel) where each of them have a mat-table with independent datasources. Sorting and pagination which is pretty easy in implementation while for a single table, is giving trouble when I am trying to do it from a mat-accordion having multiple mat-expansion-panels.
I have tried this (https://stackblitz.com/edit/data-table-multiple-data-source) method, but cannot change the type of view in code, so the dropdowns have to stay.
<div class="grid-container">
<div class="grid-list">
<mat-accordion class="expand-panel">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Case Document
</mat-panel-title>
</mat-expansion-panel-header>
<button class="add-button" mat-mini-fab matTooltip="Add Document" style="float: right">
<mat-icon class="add" (click)="addPopup('documentUpload')">add</mat-icon>
</button>
<table mat-table [dataSource]="documentDataSource" matSort>
<ng-container matColumnDef="document-name">
<th class="header" mat-header-cell *matHeaderCellDef mat-sort-header>Document Name</th>
<td mat-cell *matCellDef="let document">{{document?.name}}</td>
</ng-container>
<ng-container matColumnDef="document-category">
<th class="header" mat-header-cell *matHeaderCellDef mat-sort-header>Category</th>
<td mat-cell *matCellDef="let document">{{document?.category}}</td>
</ng-container>
<ng-container matColumnDef="document-date">
<th class="header" mat-header-cell *matHeaderCellDef mat-sort-header>Date Uploaded</th>
<td mat-cell *matCellDef="let document">{{document?.createdDate}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="documentTableColumns"></tr>
<tr mat-row *matRowDef="let document; columns: documentTableColumns"></tr>
</table>
<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons></mat-paginator>
</mat-expansion-panel>
</mat-accordion>
</div>
<div class="grid-list">
<mat-accordion class="expand-panel">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Case History
</mat-panel-title>
</mat-expansion-panel-header>
<table mat-table #historySort="matSort" [dataSource]="caseHistoryDataSource"
matSort>
<ng-container matColumnDef="type">
<th class="header" mat-header-cell mat-sort-header *matHeaderCellDef>Action</th>
<td mat-cell *matCellDef="let history">{{history?.type}}</td>
</ng-container>
<ng-container matColumnDef="key">
<th class="header" mat-header-cell mat-sort-header *matHeaderCellDef>Case Property</th>
<td mat-cell *matCellDef="let history">{{history?.key}}</td>
</ng-container>
<ng-container matColumnDef="valueName">
<th class="header" mat-header-cell mat-sort-header *matHeaderCellDef>Revised Value</th>
<td mat-cell *matCellDef="let history">{{history?.valueName}}</td>
</ng-container>
<ng-container matColumnDef="oldValueName">
<th class="header" mat-header-cell mat-sort-header *matHeaderCellDef>Previous Value</th>
<td mat-cell *matCellDef="let history">{{history?.oldValueName}}</td>
</ng-container>
<ng-container matColumnDef="createdDate">
<th class="header" mat-header-cell mat-sort-header *matHeaderCellDef>Date and Time</th>
<td mat-cell *matCellDef="let history">{{history?.createdDate | date: 'short'}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="historyTableColumns"></tr>
<tr mat-row *matRowDef="let history; columns: historyTableColumns"></tr>
</table>
<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons></mat-paginator>
</mat-expansion-panel>
</mat-accordion>
</div>
<div>
And here is the .ts file implementation.
#ViewChild(MatPaginator) historyPaginator: MatPaginator;
#ViewChild(MatSort) historySort: MatSort;
#ViewChild(MatPaginator) documentPaginator: MatPaginator;
#ViewChild(MatSort) documentSort: MatSort;
async ngAfterViewInit() {
this.caseHistoryDataSource.paginator = this.historyPaginator;
this.caseHistoryDataSource.sort = this.historySort;
this.documentDataSource.paginator = this.documentPaginator;
this.documentDataSource.sort = this.documentSort;
}
The data is visible, and showing properly, but the paginator and sorting are not working.
You should bind your pagination and sort once you loaded with data like..
this.caseHistoryDataSource=new MatTableDataSource<any>(data);
this.caseHistoryDataSource.paginator = this.historyPaginator;
this.caseHistoryDataSource.sort = this.historySort;