2
votes

Hello I using Angular Material and I trying to show in table data with following format:

{
    "error": [],
    "result": {
        "ADAUSD": {
            "asks": [
                [
                    "0.068890",
                    "22428.508",
                    1540845036
                ],
                [...]

In asks table there is another colection of tables. I need to use only first two values of this indented table. In this case 0.068890 and 22428.508. So I got two column I named it Rate and Quantity. Data is printing properly, but data is sorting only by last column.
Because in this JSON data format there is no objects and only tables it makes many problems.

Here is the component HTML:

  <table mat-table [dataSource]="dataSource" matSort (matSortChange)="sortData($event)" matSortActive="Quantity" matSortDirection="asc" matSortDisableClear class="mat-elevation-z8">

  <ng-container matColumnDef="Quantity">
    <th mat-header-cell *matHeaderCellDef mat-sort-header="Quantity"> Quantity </th>
    <td mat-cell *matCellDef="let element"> {{element[1]}} </td>
  </ng-container>

  <ng-container matColumnDef="Rate">
    <th mat-header-cell *matHeaderCellDef mat-sort-header="Rate"> Rate </th>
    <td mat-cell *matCellDef="let element"> {{element[0]}} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [pageSizeOptions]="[5]"></mat-paginator>

And component TS:

import { Component, OnInit, AfterViewInit, ViewChild } from '@angular/core';
import { MatSort, MatPaginator, MatTableDataSource, Sort} from '@angular/material';
import { OrderBookKrakenService } from 'src/app/core/order-book-kraken/order-book-kraken.service';

@Component({
  selector: 'app-kraken-sell-usd-ada',
  templateUrl: './kraken-sell-usd-ada.component.html',
  styleUrls: ['./kraken-sell-usd-ada.component.css']
})
export class KrakenSellUsdAdaComponent implements OnInit, AfterViewInit {
  displayedColumns: string[] = ['Quantity', 'Rate'];
  dataSource;
  sortedData;
  @ViewChild(MatSort) sort: MatSort;
  @ViewChild(MatPaginator) paginator: MatPaginator;

  constructor(private orderBookService: OrderBookKrakenService) {
    this.getTransfers();
  }

  ngOnInit() {
  }

  ngAfterViewInit() {
  }

  private getTransfers(): void {
    const currency1 = 'ADA';
    const currency2 = 'USD';
    this.orderBookService.getOrderBookKraken(currency1, currency2)
      .subscribe(orders => {
        if (!orders) { return; }
        console.log(orders);
        this.dataSource = orders.result.ADAUSD.bids;
        this.dataSource = this.dataSource.slice();
        //this.dataSource.sort = this.sort;
        this.sortedData.paginator = this.paginator;
      });
  }

  sortData(sort: Sort) {
    const data = this.dataSource.slice();
    if (!sort.active || sort.direction === '') {
      this.dataSource = data;
      return;
    }

    this.dataSource = data.sort((a, b) => {
      const isAsc = sort.direction === 'asc';
      switch (sort.active) {
        case 'Quantity': return compare(a, b, isAsc);
        case 'Rate': return compare(a, b, isAsc);
        default: return 0;
      }
    });
  }

}
function compare(a: number | string, b: number | string, isAsc: boolean) {
  return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}

As you can see I tried to combine mat-table and mat-sort-header. Unfortunately mat-table sorting is not working at all.

2
Both 'Quantity' and 'Rate' are using actual data item to compare? Shouldn't it be a.quantity or a.rate, as 'return compare(a.quantity, b.quantity, isAsc)'?wannadream
It makes columns not asc or desc but totally randomized.Zomfire
According to your data structure, can your try 'return compare(a[0], b[0], isAsc)' instead?wannadream

2 Answers

1
votes

Here is the solution with simple example:

HTML:

<table matSort (matSortChange)="sortData($event)">
  <tr>
    <th mat-sort-header="quantity">Quantity</th>
    <th mat-sort-header="rate">Rate</th>
    <th mat-sort-header="timestamp">Timestamp</th>
  </tr>

  <tr *ngFor="let dessert of sortedData">
    <td>{{dessert[0]}}</td>
    <td>{{dessert[1]}}</td>
    <td>{{dessert[2]}}</td>
  </tr>
</table>

TS:

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

export interface Dessert {
  listOfLists : number[][];
}
@Component({
  selector: 'sort-overview-example',
  templateUrl: 'sort-overview-example.html',
  styleUrls: ['sort-overview-example.css'],
})
export class SortOverviewExample {
  desserts: Dessert['listOfLists'] = [
    [1.00, 2500.00, 9459599599],
    [1.05, 2505.00, 9459599533],
    [1.10, 2495.00, 9459599466]
  ];

  sortedData: Dessert['listOfLists'];

  constructor() {
    this.sortedData = this.desserts.slice();
  }

  sortData(sort: Sort) {
    const data = this.desserts.slice();
    if (!sort.active || sort.direction === '') {
      this.sortedData = data;
      return;
    }

    this.sortedData = data.sort((a, b) => {
      const isAsc = sort.direction === 'asc';
      switch (sort.active) {
        case 'quantity': return compare(a[0], b[0], isAsc);
        case 'rate': return compare(a[1], b[1], isAsc);
        case 'timestamp': return compare(a[2], b[2], isAsc);
        default: return 0;
      }
    });
  }
}

function compare(a: number | string, b: number | string, isAsc: boolean) {
  return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
0
votes

If I recall, in order to attach the paginator and sorting functionality correctly it must be done after view init. This is because they must exist in the DOM for it to work correctly. It might be working for you as the constructor maybe does async work that finishes after DOM init but Id recommend moving this logic into afterviewinit. Lets say you add FE caching to your app and it will probs break.

I also think you might be better pulling the unsorted data (the actual array) out of the data source and then sorting that before using it to create a new mat data source because you are applying the array function on a matDataSource object directly no?

Sorry if that doesnt directly answer the question but if you apply this and debug it might lead to an answer.

EDIT: If you want to change how the default sorting accesses properties on the row take a look at top answer here:

Angular Material 2 DataTable Sorting with nested objects