Require help on Angular data-table with nested data.
I want to sort the data in table.
I am using data table from - https://www.npmjs.com/package/angular2-datatable
Data table works fine for single array type data. (used for many angular applications)
ISSUE: i have nested json (in reality, i have complex json, making here simple)
Thanks for looking into this.
Any suggestions or help is appreciated.
JSON
records = [
[
{
"name": "Subject Name",
"type": "text",
"id": "subjectName",
"value": "DavidJ",
"firstName": "David",
"lastName": "John"
},
{
"name": "QC Name",
"type": "hidden",
"id": "qcName",
"value": "JosephT",
"firstName": "Joseph",
"lastName": "Tom"
}
],
[
{
"name": "Subject Name",
"type": "text",
"id": "subjectName",
"value": "TigerC",
"firstName": "Tiger",
"lastName": "Chan"
},
{
"name": "QC Name",
"type": "hidden",
"id": "qcName",
"value": "ThomasR",
"firstName": "Thomas",
"lastName": "Richard"
}
]
]
HTML
<table class="table table-responsive table-hover" [mfData]="this.records | dataFilter : filterQuery" #mf="mfDataTable" [mfRowsOnPage]="rowsOnPage" [(mfSortBy)]="sortBy" [(mfSortOrder)]="sortOrder">
<thead>
<tr>
<th>#</th>
<th>
<mfDefaultSorter by="subjectName">subject Name</mfDefaultSorter>
</th>
<th>
<mfDefaultSorter by="qcPerson">QC Person</mfDefaultSorter>
</th>
</tr>
</thead>
<tbody *ngIf="!isLoading">
<tr class="border" *ngFor="let sample of mf.data; let i='index'">
<td>{{i + 1}}</td>
<ng-container *ngFor="let item of sample">
<td *ngIf="item.id ==='subjectName'">
{{item.firstName}} {{item.lastName}}
</td>
<td *ngIf="item.id ==='qcPerson'">
{{item.firstName}} {{item.lastName}}
</td>
</ng-container>
</tr>
</tbody>
</table>
TYpescript file
import { Component, OnInit } from '@angular/core';
import { OrderBy } from '../all_services/OrderByPipe';
@Component({
selector: 'app-userdashboard',
templateUrl: './userdashboard.component.html',
styleUrls: ['../header-footer/css/external.style.css']
})
export class UserdashboardComponent implements OnInit {
constructor() {}
ngOnInit() {}
/** Sorting functions */
public data;
public filterQuery = "";
public rowsOnPage = 10;
public sortBy = "subjectName";
public sortOrder = "asc";
public toInt(num: string) {
return +num;
}
}
Datafilterpipe.ts
import * as _ from "lodash";
import {Pipe, PipeTransform} from "@angular/core";
@Pipe({
name: "dataFilter"
})
export class DataFilterPipe implements PipeTransform {
transform(array: any[], query: string): any {
if (query) {
return _.filter(array, row=>row.name.indexOf(query) > -1);
}
return array;
}
}
I had face the same issue. I had used w3school table sort logic once the table is rendered in DOM.
It works really smooth with angular2-datatable as I am using the same datatable in my project. Its usage is very straight, stillif you face any issue please let me know.
Thanks in advance.
Below will be implementation Function in your TS file.
columnSorter(n) {
let table, rows, switching, i, x, y, shouldSwitch, dir, switchCount = 0;
switching = true;
this.clickSort = !this.clickSort
dir = "asc";
table = document.querySelector('.smallTable');
while (switching) {
switching = false;
rows = table.rows;
for (i = 0; i < (rows.length - 1); i++) {
shouldSwitch = false;
x = rows[i].getElementsByTagName("TD")[n];
y = rows[i + 1].getElementsByTagName("TD")[n];
if (dir == 'asc') {
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
shouldSwitch = true;
break;
}
} else if (dir == 'desc') {
if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
shouldSwitch = true;
break;
}
}
}
if (shouldSwitch) {
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
switchCount++;
} else {
if (switchCount == 0 && dir == 'asc') {
dir = 'desc';
switching = true;
}
}
}
}
In you HTML table on th just add below, where 0 is column no.
(click)="columnSorter(0)
Using this library you have to use these input variables to sort table once:
mfSortBy: any - sort by parameter
mfSortOrder: string - sort order parameter, "asc" or "desc"
Also you can add this tag to you th to allow user to sort it by click:
<mfDefaultSorter by="name">Name</mfDefaultSorter>
To create custom sort for the table you just need to sort yours json. In your case you should operate with what you assign to mf.data.
You can create custom derective where you will create sorter for a table and then sort data by click.
e.g.
import {
Directive, ElementRef, AfterViewChecked,
Input, Output, Renderer, EventEmitter
} from '@angular/core';
@Directive({
selector: '[sorter], [defaultSorter]'
})
export class TableSorterDerective implements AfterViewChecked {
@Input()
sorter: {order:string, property:string};
@Output()
sorted = new EventEmitter();
constructor(private el: ElementRef, private renderer: Renderer) {
}
ngAfterViewChecked() {
let element: HTMLElement = this.el.nativeElement;
if(this.sorter){
this.addSorter(element);
}
}
addSorter(column: HTMLElement){
if(!column.classList.contains("custom_sorter")){
column.addEventListener('click', () => this.sendSort(column), false)
column.classList.add("custom_sorter");
}
}
sendSort(element:HTMLElement){
let columns: HTMLElement[] =
Array.prototype.slice.call(element.parentElement.getElementsByTagName('th'), 0);
columns.forEach(element => {
if(!element.classList.contains(this.sorter.property)){
let icon = element.getElementsByTagName('span')[0];
if(icon) icon.remove();
}
});
let icon:HTMLElement = element.getElementsByTagName('span')[0];
if(!icon) icon = this.renderer.createElement(element, 'span');
icon.classList.remove("glyphicon-triangle-bottom")
icon.classList.remove("glyphicon-triangle-top")
icon.classList.remove("glyphicon")
if(this.sorter.order == "asc"){
this.sorter = {order:"desc", property:this.sorter.property}
icon.classList.add("glyphicon")
icon.classList.add("glyphicon-triangle-top")
}else if(this.sorter.order == "desc"){
this.sorter = {order:"asc", property:this.sorter.property}
icon.classList.add("glyphicon")
icon.classList.add("glyphicon-triangle-bottom")
}
this.sorted.emit(this.sorter)
}
}
and then you just need to sort data on emit:
<th *ngFor="let col of columns" [sorter]="col.sorting ? {order:'desc', property:col.property} : undefined" (sorted)="transformationsService.sort(filteredData, $event)"</th>
To sort data just use sort function e.g. :
data.sort((a, b) => {
return 0 - (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1)
}
See this question if you need help with sort function.
To sort the data you could always use Lodash's very powerful _.sortBy
:
import * as _ from 'lodash';
const nestedData = [
{ a: { b: 2} },
{ a: { b: 1} },
{ a: { b: 3} }
];
// Outputs [ { a: { b: 1 } }, { a: { b: 2 } }, { a: { b: 3 } } ]
_.sortBy(nestedData, item => _.get(item, ['a', 'b']));
It even supports sorting by multiple fields:
import * as _ from 'lodash';
const nestedData = [
{ a: { b: 2 }, c: 'a' },
{ a: { b: 1 }, c: 'b' },
{ a: { b: 1 }, c: 'd'}
];
// Output:
// [ { a: { b: 1 }, c: 'b' },
// { a: { b: 1 }, c: 'd' },
// { a: { b: 2 }, c: 'a' } ]
_.sortBy(nestedData, [item => _.get(item, ['a', 'b']), item => _.get(item, 'c')]);
As far as adding this to your current table -- there are a lot of ways you can achieve that. It might be easier to pre-sort your data before you pass it the table. If the user clicks a column header -- simply run the data through the appropriate _.sortBy
and dump it back into the table?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With