Angular: Mat Sort not working for table like expansion

Viewed 1604

I am trying to sort the table but it's not getting sort. I have found the problem for not happening. It's not happening because mat-sort-header is not matching with the model. But the problem is my JSON object is not the same as this example. Also for the name part, my data is coming from firstName, middleName, lastName and I have to show it as Name only. Let me show my JSON object and my HTML below:

JSON object received from API

[
    {
        "exam": {
            "examCategory": {
                "examCategoryName": "Freshers",
                "id": 157,
                "timestamp": "2020-07-07 11:55:22.425"
            },
            "examName": "First Demo",
            "timestamp": "2020-07-07 15:17:32.0"
        },
        "id": 94,
        "user": {
            "id": 9,
            "firstName": "AVISHEK",
            "middleName": "DATTA",
            "lastName": "RAY",
            "timestamp": "2020-07-07 15:06:58.186"
        },
        "timestamp": "2020-07-07 13:15:24.898"
    }
]

example.component.html

isplayMode="flat" class="mat-table" [class.d-none]="isLoading || length==0">
  <section matSort class="mat-elevation-z2 mat-header-row">
    <span class="mat-header-cell" mat-sort-header="user.id">User Id</span>
    <span class="mat-header-cell" mat-sort-header="name">Name</span>
    <span class="mat-header-cell" mat-sort-header="examName">Exam Name</span>
    <span class="mat-header-cell" mat-sort-header="examCategoryName">Exam Category</span>
    <span class="mat-header-cell" mat-sort-header="marks">Marks Obtained</span>
  </section>
  <mat-expansion-panel *ngFor="let result of selectedResult | async">
    <mat-expansion-panel-header class="mat-row">
      <span class="mat-cell">{{ result.user.id }}</span>
      <span class="mat-cell">
        {{ result.user.firstName | titlecase }} 
        {{ result.user.middleName | titlecase }} 
        {{ result.user.lastName | titlecase }}
      </span>
      <span class="mat-cell">{{ result.exam.examName }}</span>
      <span class="mat-cell">{{ result.exam.examCategory.examCategoryName }}</span>
    </mat-expansion-panel-header>
    <div class="row">
      // Any Body //
    </div>
  </mat-expansion-panel>
</mat-accordion>

As in TS I have declared selectedResult as selectedResult : Observable<any[]> I have given mat-sort-header names on my own. What change do I need to make the sorting work?

4 Answers

I am not sure.. but you can try like this...

When you received JSON object from API

In example.component.ts

undortedData: any;
mainData: any;
this.mainData = [
 {
    "exam": {
        "examCategory": {
            "examCategoryName": "Freshers",
            "id": 157,
            "timestamp": "2020-07-07 11:55:22.425"
        },
        "examName": "First Demo",
        "timestamp": "2020-07-07 15:17:32.0"
    },
    "id": 94,
    "user": {
        "id": 9,
        "firstName": "AVISHEK",
        "middleName": "DATTA",
        "lastName": "RAY",
        "timestamp": "2020-07-07 15:06:58.186"
    },
    "timestamp": "2020-07-07 13:15:24.898"
 }
];

this.undortedData = [...this.mainData];

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

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

  }

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

In your example.component.html

</mat-accordion> isplayMode="flat" class="mat-table" [class.d-none]="isLoading || length==0">
  <section matSort (matSortChange)="sortData($event)" class="mat-elevation-z2 mat-header-row">
    <span class="mat-header-cell" mat-sort-header="user.id">User Id</span>
    <span class="mat-header-cell" mat-sort-header="name">Name</span>
    <span class="mat-header-cell" mat-sort-header="examName">Exam Name</span>
    <span class="mat-header-cell" mat-sort-header="examCategoryName">Exam Category</span>
    <span class="mat-header-cell" mat-sort-header="marks">Marks Obtained</span>
  </section>
  <mat-expansion-panel *ngFor="let result of selectedResult | async">
    <mat-expansion-panel-header class="mat-row">
      <span class="mat-cell">{{ result.user.id }}</span>
      <span class="mat-cell">
        {{ result.user.firstName | titlecase }} 
        {{ result.user.middleName | titlecase }} 
        {{ result.user.lastName | titlecase }}
      </span>
      <span class="mat-cell">{{ result.exam.examName }}</span>
      <span class="mat-cell">{{ result.exam.examCategory.examCategoryName }}</span>
    </mat-expansion-panel-header>
    <div class="row">
      // Any Body //
    </div>
  </mat-expansion-panel>
</mat-accordion>
  <section matSort class="mat-elevation-z2 mat-header-ro (matSortChange)="sortData($event)>
    <span class="mat-header-cell" mat-sort-header="user.id">User Id</span>
    <span class="mat-header-cell" mat-sort-header="name">Name</span>
    <span class="mat-header-cell" mat-sort-header="examName">Exam Name</span>
    <span class="mat-header-cell" mat-sort-header="examCategoryName">Exam Category</span>
    <span class="mat-header-cell" mat-sort-header="marks">Marks Obtained</span>
  </section>

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

Like this you have to set matSortChange Event in to your section. it should work after apply this

You just need to modify the sortData(sort: Sort) code given in sort header angular material documentation given according to the structure of your data.

  1. Change the mat-sort-header="user.id" to mat-sort-header="userId". The text will be needed only to match cases in your sortData(sort: Sort), hence doesn't have to be dotted notation. Also, I'm not sure if it will work with dotted notation.

  2. Modify the inputs to the compare function in the switch cases. Like for examName pass on compare(a.exam.examName, b.exam.examName, isAsc) i.e in accordance to your JSON data structure.

  3. Getting the name row sorted is a bit tricky for name thing. In html you are using string interpolation to display the concatenated names, similarly use template literals to pass concatenated names to the subsequent compare function.

Working Stackblitz for your understanding.

Note:- Haven't added the marks part since the api response doesn't have any marksObtained property, but I'm guessing it's a number, just pass along similar to the userid.

You have mentioned that your JSON is not correct and let's assume you have corrected it for yourself as of now. Also, it looks like you want an expandable row(not really mat-expansion-panel) with matSort functionality

Use Table with expandable rows link

Related