Search code examples
angularhtml-tableangular-materialtablesorter

angular 4.4: can't sort by some columns of a table


I want to sort a table by its content, I have several columns, the sorting code works on some of them, but in the rest, the sorting code do not work.

I get the data from an API rest. The data is obtained correctly, and displayed in the table correctly.

First I have Students object, and I have Points object.

Students are like:

[{
  "id": 10000,
  "name": "Somebody",
  "points": {"1": 5, "2":0, "3": 10}
},{
  "id": 10001,
  "name": "JustAnother",
  "points": {"1":0, "2": 1, "3": 4}
}]

And Points object is like:

[{
  "id": 1,
  "name": "foo",
  "value": 2
},
{
  "id": 2,
  "name": "bar",
  "value": 5
},
{
  "id": 3,
  "name": "baz",
  "value": 1
}]

Then I display them on the table like this:

<mat-card *ngIf="sortedData">
  <table matSort (matSortChange)="sortData($event)">
    <tr>
      <th mat-sort-header="id">Id</th>
      <th mat-sort-header="name">name</th>
      <th mat-sort-header="{{point.id}}" *ngFor="let point of Points">{{point.name}}</th>
    </tr>

    <tr *ngFor="let student of sortedData">
      <td>{{student.id}}</td>
      <td>{{student.name}}</td>
      <td *ngFor="let point of Points" class="center">
        {{student.points[point.id]}}
      </td>
    </tr>
  </table>
</mat-card>

the table is like:

id    | name    | foo    | baz    | bar
------------------------------------------
10000 | Somebody| 5      | 0      | 10
10001 | JustAnot| 0      | 1      | 4

And the sorting code in the table component is:

public sortedData: Student[];
this.sortedData = this.Students;  // <- this is inside the response of get students function.

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

  this.sortedData = data.sort((a, b) => {
    const isAsc = sort.direction === 'asc';
    const points_array = this.Points; // <- this.points contains the Points object
    if (sort.active === 'id') {
      return compare(a.id, b.name, isAsc);
    }
    if (sort.active === 'name') {
      return compare(a.name, b.name, isAsc);
    }
    Object.keys(points_array).forEach(
      function (index) {
        if (parseInt(points_array[index].id, 10) === parseInt(sort.active, 10)) {
          return compare(
            a['points'][ points_array[index].id ],
            b['points'][ points_array[index].id ],
            isAsc
          );
        }
      }
    );
  });
}

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

I have more properties and columns, it's simplified for the example. All the columns like id or name can be sorted without any problem.

But the point columns can't be sorted. When I click on one of that columns i makes like sorting, but the order remains unchanged.

If I log to the sorting functions, I see that the comparison is made, and the response of the compare function is 0 or -1, the same as the columns that are sorted correctly.

There is no javascript error on console, apparently it works, but when I try to sort the data by some of the columns point, the order remains the initial.

Why is not sorting by the point columns?

Edit: a working stackblitz replicating the issue: https://stackblitz.com/edit/angular-cruuo3

Edit 2: I added to the stackblitz some more students and another column with 'level' to sort. 'Level' is working fine, as 'id' and 'name'.


Solution

  • Try this code, it is work:

    return compare(a['points'][ points_array[parseInt(sort.active, 10)].id ], 
    b['points'][ points_array[parseInt(sort.active, 10)].id ], isAsc);
    

    the problem is in forEach callback function return value but foreach parnt return a void value .

    for example for do work ,because in not use call back function

    for (const index of Object.keys(points_array)) { 
      if (parseInt(points_array[index].id, 10) === parseInt(sort.active, 10)) { 
        return compare( 
          a['points'][points_array[index].id], 
          b['points'][points_array[index].id], 
          isAsc 
        ); 
      } 
    }