javascripthtmlangularsortingprimeng

Dynamic column sorting in angular primeNg


I am rendering the table using angular primeNg based on a condition. All the headers and values are coming dynamically. The table data is getting displayed properly. However the sorting is not working. I have used [pSortableColumn]="header". I can see the icons are moving upwords and downwards but the values are sorted. Attaching the code and data for reference.

 getColumnHeaders(dataList: any[]): string[] { 
   if (dataList.length > 0) { 
     return Object.keys(dataList[0]); 
   } else { 
     return []; 
   } 
 }
  <div class="card-tablelist mb-3" *ngFor="let singleObject of Data; index as i">
  <div *ngIf="singleObject.DashboardDto.alertType!='Hybrid'">
                            <p-card class="alert-cardslist"
                                *ngIf="singleObject.DashboardDto as singleKey">
                                <ng-template pTemplate="header">
                                   Alerts
                                </ng-template>
                                <ng-template pTemplate="content">
                                    <div class="grid-data">
                                        <p-table #dt1 [value]="singleKey.dataList" styleClass="p-datatable-gridlines"
                                            [tableStyle]="{'min-width': '50rem'}" [sortable]="true">
                                            <ng-template pTemplate="header">
                                                <tr>
                                                    <th 
                                                        *ngFor="let header of getColumnHeaders(singleKey.dataList)" [pSortableColumn]="header">
                                                        {{header}} <p-sortIcon [field]="header"></p-sortIcon> </th>
                                                </tr>
                                            </ng-template>
                                            <ng-template pTemplate="body" let-data>
                                                <tr>
                                                    <td [ngClass]="{'text-left': data[header].alignment==='Left', 'text-right':data[header].alignment==='Right'}"
                                                        *ngFor="let header of getColumnHeaders(singleKey.dataList)">
                                                        {{data[header].value}}
                                                    </td>
                                                </tr>
                                            </ng-template>
                                        </p-table>
                                    </div>
                                </ng-template>
                            </p-card>
                        </div>
                        </div>

{ 
 "data": [ 
   {
    DashboardDto :{
      "companyName": "ABC Company Limited",
      "alertType": "Standard",
      "alertTypeComputed": "High",
      "isCriticalAlert": 1,
      "dataList": [
        {
          "Company Name": {
            "value": ABC Company Limited",
            "alignment": "Left"
          },
          "Unit": {
            "value": "Lakh",
            "alignment": "Left"
          },
          "Ratio": {
            "value": "0",
            "alignment": "Right"
          },
          "Period Ends on": {
            "value": "31-Mar-2025",
            "alignment": "Left"
          }
        },
        {
          "Company Name": {
            "value": "ABC Company Limited",
            "alignment": "Left"
          },
          "Unit": {
            "value": "Crore",
            "alignment": "Left"
          },
          "Ratio": {
            "value": "0",
            "alignment": "Right"
          },
          "Period Ends on": {
            "value": "31-Mar-2024",
            "alignment": "Left"
          }
        },
        {
          "Company Name": {
            "value": "ABC Company Limited",
            "alignment": "Left"
          },
          "Unit": {
            "value": "Lakh",
            "alignment": "Left"
          },
          "Ratio": {
            "value": "1",
            "alignment": "Right"
          },
          "Period Ends on": {
            "value": "31-Mar-2023",
            "alignment": "Left"
          }
        }
      ]
    }
}
]
}

I am new to angular looking for suggestions. Thanks in advance.


Solution

  • This happens because your data is structured as:

    {
      "Company Name": {
        "value": "ABC Company Limited",
        "alignment": "Left"
      }
    }
    

    But PrimeNG sorting expects flat fields, like:

    {
      "Company Name": "ABC Company Limited"
    }
    

    pSortableColumn is pointing to header (e.g., "Company Name"), but it tries to sort using the raw object

    You need to transform dataList into a flat format, where each cell is just the .value. For example:

    [
      {
        "Company Name": "ABC Company Limited",
        "Unit": "Lakh",
        "Ratio": "0",
        "Period Ends on": "31-Mar-2025"
      },
      //...your other objects
    ]
    

    Use transformDataList before passing to p-table

    <p-table
      #dt1
      [value]="transformDataList(singleKey.dataList)"
      [tableStyle]="{ 'min-width': '50rem' }"
      [sortable]="true"
      styleClass="p-datatable-gridlines"
    >
      <ng-template pTemplate="header">
        <tr>
          <th
            *ngFor="let header of getColumnHeaders(singleKey.dataList)"
            [pSortableColumn]="header"
          >
            {{ header }}
            <p-sortIcon [field]="header"></p-sortIcon>
          </th>
        </tr>
      </ng-template>
    
      <ng-template pTemplate="body" let-data>
        <tr>
          <td
            *ngFor="let header of getColumnHeaders(singleKey.dataList)"
            class="text-left"
          >
            {{ data[header] }}
          </td>
        </tr>
      </ng-template>
    </p-table>
    

    Add a transformer

    transformDataList(dataList: any[]): any[] {
      return dataList.map(row => {
        const flatRow: any = {};
        for (const key in row) {
          if (row[key] && typeof row[key] === 'object' && 'value' in row[key]) {
            flatRow[key] = row[key].value;
          } else {
            flatRow[key] = row[key]; // fallback
          }
        }
        return flatRow;
      });
    }
    

    By this your sorting will work, but you lose the .alignment info in this approach. If you still need it (e.g., to set left/right alignment), you can store the raw object separately and use it only for styling, not data rendering.