Prime NG Row Group with Subheader

Prime NG Row Group with Subheader

Table – RowGroup


It is easy to implement row grouping using the flexible template-driven approach of the p-table. In this example, sorting is enabled by default to sort the data by brand initially and then a rowGroupMetadata object is created to represent how many rows a brand should span along with the rowIndex of the group. Similarly, multiple field grouping can be implemented as well.


Points covered in this blog:

  1. We have created PRIME NG table using Angular framework.
  2. Implemented the Sorting feature of Prime Ng.
  3. Implemented Row Grouping with Subheader.
  4. Grouped by a region field.
  5. Display Total Row as the first row of each group.

Code

app.component.html

This is HTML template contains Prime Ng Table with Angular framework.
<div class="card">
  <h5>Region Summary Data with Subheader, Grouping & total row</h5>
  <p-table
    [value]="summary"
    [columns]="summaryTableColumns"
    (sortFunction)="customSort($event)"
    [customSort]="true"
  >
    <ng-template pTemplate="header" let-columns>
      <tr>
        <th *ngFor="let col of columns" [pSortableColumn]="col.field">
          {{ col.header }}
          <p-sortIcon
            [field]="col.field"
            ariaLabel="Activate to sort"
            ariaLabelDesc="Activate to sort in descending order"
            ariaLabelAsc="Activate to sort in ascending order"
          ></p-sortIcon>
        </th>
      </tr>
    </ng-template>
    <ng-template pTemplate="body" let-summary let-rowIndex="rowIndex">
      <tr
        class="subheader-row"
        *ngIf="rowGroupMetadata[summary.region].index === rowIndex"
      >
        <td *ngFor="let col of summaryTableColumns; let colIndex = index">
          <span
            *ngIf="col.field === 'region'; else displayTotalFields"
            style="font-weight: bold"
            >{{ summary.region }}</span
          >
          <ng-template #displayTotalFields>
            <span class="total">{{
              rowGroupMetadata[summary.region][col.field]
            }}</span>
          </ng-template>
        </td>
      </tr>
      <tr>
        <td></td>
        <ng-container
          *ngFor="let col of summaryTableColumns; let colIndex = index"
        >
          <td *ngIf="col.field !== 'region'">
            <span>{{ summary[col.field] }}</span>
          </td>
        </ng-container>
      </tr>
    </ng-template>
  </p-table>
</div>

app.component.css

This css file contains styling.
.subheader-row {
    background-color:antiquewhite !important;
}

.total {
 font-weight: bold;
}

app.component.ts

This typescript file contains code to get summary data from a JSON file and custom sort functionality and row grouping.
import { Component } from '@angular/core';
import { Summary } from './summary.model';
import { SummaryService } from './summary.service';
import { SortEvent } from 'primeng/api';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  summary: Summary[];

  rowGroupMetadata: any;
  summaryTableColumns = [
    {
      field: 'region',
      header: 'Region',
    },
    {
      field: 'city',
      header: 'City',
    },
    {
      field: 'hospitals',
      header: 'Total Hospitals',
    },
    {
      field: 'schools',
      header: 'Total Schools',
    },
    {
      field: 'colleges',
      header: 'Total Colleges',
    },
    {
      field: 'companies',
      header: 'Total Companies',
    },
  ];

  constructor(private SummaryService: SummaryService) {}

  /**
   * Hit an API to get summary data
   * call updateRowGroupMetaData to create sub header and grouping
   */
  ngOnInit() {
    this.SummaryService.getCustomersMedium().then((data) => {
      this.summary = data;
      this.updateRowGroupMetaData();
    });
  }

  /**
   * Sorting data
   * @param event
   */
  customSort(event: SortEvent) {
    event.data.sort((data1, data2) => {
      let value1 = data1[event.field];
      let value2 = data2[event.field];
      let result = null;

      if (value1 == null && value2 != null) result = -1;
      else if (value1 != null && value2 == null) result = 1;
      else if (value1 == null && value2 == null) result = 0;
      else if (data1.region === data2.region && data1.city && data2.city) {
        if (typeof value1 === 'string' && typeof value2 === 'string') {
          result = value1.localeCompare(value2);
        } else {
          result = value1 < value2 ? -1 : value1 > value2 ? 1 : 0;
        }
      } else if (event.field === 'region') {
        result = value1.localeCompare(value2);
      } else {
        result = 0;
      }
      return event.order * result;
    });
    this.updateRowGroupMetaData();
  }

  /**
   * Updates Rows with Groups
   */
  updateRowGroupMetaData() {
    this.rowGroupMetadata = {};

    if (this.summary) {
      for (let i = 0; i < this.summary.length; i++) {
        let rowData = this.summary[i];

        const totalRowObj = {
          index: 0,
          size: 1,
          hospitals: rowData.hospitals,
          schools: rowData.schools,
          colleges: rowData.colleges,
          companies: rowData.companies,
        };

        if (i == 0) {
          this.rowGroupMetadata[rowData.region] = totalRowObj;
        } else {
          let previousRowData = this.summary[i - 1];
          if (rowData.region === previousRowData.region)
            this.rowGroupMetadata[rowData.region].size++;
          else
            this.rowGroupMetadata[rowData.region] = {
              ...totalRowObj,
              index: i,
            };
        }
      }
    }
    console.log(this.rowGroupMetadata);
  }
}

summary.service.ts

This is service file contans http request to fetch data.
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Summary } from './summary.model';

@Injectable()
export class SummaryService {
  constructor(private http: HttpClient) {}

  getCustomersMedium() {
    return this.http
      .get<any>('assets/summary-data.json')
      .toPromise()
      .then((res) => <Summary[]>res.data)
      .then((data) => {
        return data;
      });
  }
}

summary.model.ts

This is view model defines Summary fields.
export interface Summary {
    region? : string;
    city?:string;
    hospitals?: number;
    schools?: number;
    colleges?: number;
    companies?: number;
}

summary-data.json file

This is JSON file.

https://github.com/msaxena25/Prime-NG-Row-Group-with-Subheader-and-Totals-Row/blob/main/src/assets/summary-data.json



Complete Code on Git hub

https://github.com/msaxena25/Prime-NG-Row-Group-with-Subheader-and-Totals-Row

https://www.jsmount.com/how-to-implement-reactive-form-with-prime-ng-table/

Learn document: https://www.primefaces.org/primeng-8.1.5/#/table/rowgroup

VS Code Useful Extensions for Web Development

Leave a Reply

Your email address will not be published.