Угловая сортировка таблицы данных из вложенного json

Требуется помощь по таблице угловых данных с вложенными данными.

Я хочу сортировать данные в таблице.

Я использую таблицу данных - https://www.npmjs.com/package/angular2-datatable

Таблица данных отлично работает для данных типа одиночного массива. (используется для многих угловых применений)

ISSUE: у меня есть вложенный json (на самом деле у меня сложный json, делающий здесь простое)

Спасибо, что посмотрели на это.

Любые предложения или помощь приветствуются.

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

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;
    }
}

Ответы

Ответ 1

У меня была такая же проблема. Я использовал логику сортировки таблицы w3school после отображения таблицы в DOM.

Он работает очень плавно с угловым2-datatable, поскольку я использую то же самое datatable в моем проекте. Его использование очень прямое, но если вы столкнулись с какой-либо проблемой, пожалуйста, дайте мне знать.

Заранее спасибо.

Ниже будет функция реализации в вашем файле TS.

  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;
        }
      }
    }
  }

В вашей таблице HTML на th просто добавьте ниже, где 0 - столбец no.

(click)="columnSorter(0)

Ответ 2

Используя эту библиотеку, вы должны использовать эти входные переменные для сортировки таблицы один раз:

mfSortBy: any - sort by parameter
mfSortOrder: string - sort order parameter, "asc" or "desc"

Также вы можете добавить этот тег к вам, чтобы разрешить пользователю сортировать его по клику:

<mfDefaultSorter by="name">Name</mfDefaultSorter>

Чтобы создать пользовательский сортировку для таблицы, вам просто нужно отсортировать ваш json. В вашем случае вы должны работать с тем, что вы назначили mf.data.

Вы можете создать собственный derective, где вы создадите сортировщик для таблицы, а затем отсортируйте данные по клику.

например

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)
  }
}

а затем вам просто нужно отсортировать данные об испускании:

    <th *ngFor="let col of columns" [sorter]="col.sorting ? {order:'desc', property:col.property} : undefined" (sorted)="transformationsService.sort(filteredData, $event)"</th>

Для сортировки данных просто используйте функцию сортировки, например:

data.sort((a, b) => {
        return 0 - (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1)
      }

См. Этот вопрос, если вам нужна помощь с функцией сортировки.

Ответ 3

Чтобы отсортировать данные, вы всегда можете использовать Lodash очень мощный _.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']));

Он даже поддерживает сортировку по нескольким полям:

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')]);

Что касается добавления этого в текущую таблицу, вы можете достичь этого. Возможно, было бы проще предварительно отсортировать данные перед тем, как передать таблицу. Если пользователь нажимает заголовок столбца - просто запустите данные через соответствующий _.sortBy и _.sortBy его обратно в таблицу?

Ответ 4

Вместо использования dataTable вы можете реализовать пользовательский канал, который будет сортировать данные без изменения исходного объекта. Некоторые способы сортировки данных при нажатии на столбцы. Вот простой код, надеюсь, что это сработает для вас.

import { Pipe, PipeTransform } from "@angular/core";
@Pipe({
  name: "orderby",
  pure: false
})
export class OrderByPipe implements PipeTransform {
  transform(array: Array<any>, args?: any) {
    let newDataArray:any = [];      
        if (array.length > 0 && args.value != undefined) {
        for (let item of array) {
            for (let subItem of item) {
                newDataArray.push(subItem);
            }
        }
        newDataArray.sort((a: any, b: any) => {
                    if ((a.firstName).toLowerCase() < (b.firstName).toLowerCase()) {
                        return -1 * args.direction;
                    } else if ((a.firstName).toLowerCase() > (b.firstName).toLowerCase()) {
                        return 1 * args.direction;
                    } else {
                        return 0;
                    }
                });
        array = Array.from(newDataArray);  
        }
       return array;
     }
    }           

Чтобы вызвать этот канал из кода использования вашего компонента, как показано ниже, определите эти переменные

isDesc: boolean = false;
direction: number = 1;
filterData = [];

а также

  orderByMe(toBeSorted: string){
     this.isDesc = !this.isDesc;
     this.direction = this.isDesc ? 1 : -1;
     this.filterData = Array.from(new OrderByPipe().transform( this.records, { 
     value: toBeSorted, direction: this.direction } ));
   } 

  ngOnInit(){
    this.orderByMe('subjectName');
  }

В шаблоне Html вы можете использовать

<table>
<thead>
<tr>
  <th>#</th>
  <th (click)="orderByMe('subjectName')">subjectName</th>
  <th (click)="orderByMe('qcName')">qcPerson</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let record of filterData; let currentIndex = index;">
  <td>{{ currentIndex + 1}}</td>
  <td>{{ record.firstName }}</td>
  <td>{{ record.lastName }}</td>
</tr>
</tbody>
</table>