Custom sorting for datatable


Topic: Custom sorting for datatable

mpronkbolcom premium asked 2 months ago

We use mdbootstrap datable to show an overview of vulnerabilities, and sort on severity.

Expected behavior

Ability to overide sort logic so I can sort on severity (e.g. to make it sort on severity as Critical/High/Medium/Low/Informational as descending)

Actual behavior

I cannot override the datatable sort logic (so the best result i currently have is sorting on severity as ascending - which gives Critical/High/Informational/Medium/Low )

Resources (screenshots, code snippets etc.)https://mdbootstrap.com/docs/b5/angular/data/datatables/#docsTabsAPI


Arkadiusz Idzikowski staff commented 2 months ago

@mpronkbolcom Could you provide an example of a use case that you need to implement? What exactly do you need to override in the current sorting logic?


mpronkbolcom premium commented 2 months ago

hi @Arkadiusz Idzikowski, Thanks for the quick reply. I've tried to give the use case and examples above. Im creating a vulnerability dashboard using angular, and these vulnerabilities are shown in a mdbootstrap datatable. I sort the rows on severity ascending, but thats the wrong order (see expected/actual behavior). I want to implement custom sorting logic to sort it how I want.

Please let me know if this clarifies my problem

I found _sortFn here, but no documentation; https://git.mdbootstrap.com/mdb/angular/mdb5/prd/mdb5-angular-ui-kit-pro-essential/-/blob/master/table/table.directive.d.ts#L58


Arkadiusz Idzikowski staff answered 2 months ago

You can try to disable to custom custom sorting behavior by removing [sort]="sort" from the element with mdbTableDirective. Then you can listen to (sortChange) event and modify table data using your custom sorting function. Here is an example (you need to create a custom sortFn function or completely modify onSortChange function logic), the MdbSortChange returns information about the active sort header and sorting direction.

HTML:

  <table
    class="table datatable-table"
    mdbTable
    mdbTableSort
    #table="mdbTable"
    (sortChange)="onSortChange($event)"
    [dataSource]="dataSource"
    [pagination]="pagination"
  >

TS:

@ViewChild('table') table: MdbTableDirective<Person>;

  onSortChange(event: MdbSortChange): void {
    const { name, direction } = event;

    if (direction !== 'none') {
      this.dataSource.sort((a, b) => {
        const result = this._sortFn(a[name], b[name]);
        return direction === 'asc' ? result : -result;
      });
    }

    this.dataSource = [...this.dataSource];
  }

Edit:

Replaced this.table.data with this.dataSource in sort function to include all rows (not only rows visible in current view).


mpronkbolcom premium commented 2 months ago

hi @Arkadiusz IdzikowskiThanks! I was able to create the custom sorting logic. Unfortunately, it only sorts the rows on the current page. Instead, It should take all rows into account when ordering. Can you help a bit more ?

This is the sorting logic I have:

onSortChange(event: MdbSortChange): void {
const { name, direction } = event;

if (direction !== 'none') {
  console.log(`${name} - ${direction}`);

  if (name == 'severity') {
    console.log('sorting on severity');
    this.table.data.sort((a, b) => {
      const order = [];
      for (let key in Severity) {
        order.push(key);
      }

      var textA = order.findIndex(
        (key) => Severity[key as keyof typeof Severity] === a.severity
      );
      var textB = order.findIndex(
        (key) => Severity[key as keyof typeof Severity] === b.severity
      );

      const result = textA < textB ? -1 : textA > textB ? 1 : 0;
      return direction === 'asc' ? result : -result;
    });
  } else {
    this.table.data.sort((a, b) => {
      var textA = a[name as keyof Finding]?.toString() ?? '';
      var textB = b[name as keyof Finding]?.toString() ?? '';
      const result = textA < textB ? -1 : textA > textB ? 1 : 0;
      return direction === 'asc' ? result : -result;
    });
  }
}

}

With the following table definition:

  <table
    class="table datatable-table"
    mdbTable
    mdbTableSort
    #table="mdbTable"
    #sortSearch="mdbTableSort"
    [fixedHeader]="true"
    [dataSource]="findings$ | async"
    (sortChange)="onSortChange($event)"
    [pagination]="paginationSearch"
    [filterFn]="filterFn"
  >
    <thead class="datatable-header">
      <tr>
        <th scope="col">
          <div class="form-check d-flex align-items-center mb-0">
            <input
              mdbCheckbox
              class="datatable-header-checkbox form-check-input"
              type="checkbox"
              [checked]="allRowsSelected()"
              (checkboxChange)="toggleAll($event)"
            />
          </div>
        </th>
        <th
          *ngFor="let header of headers"
          [mdbTableSortHeader]="header"
          scope="col"
        >
          {{ header | titlecase }}
        </th>
      </tr>
    </thead>

Arkadiusz Idzikowski staff commented 2 months ago

@mpronkbolcom Please try to sort dataSource (or its copy) instead of this.table.data and make sure to update dataSource reference so the table directive is notified about the change in data. I edited my answer and included these changes.


mpronkbolcom premium answered 2 months ago

Thanks it works, awesome!:

  if (this.table.dataSource) {
    if (name == 'severity') {
      console.log('sorting on severity');
      this.table.dataSource.sort((a, b) => {
        const order = [];
        for (let key in Severity) {
          order.push(key);
        }

        var textA = order.findIndex(
          (key) => Severity[key as keyof typeof Severity] === a.severity
        );
        var textB = order.findIndex(
          (key) => Severity[key as keyof typeof Severity] === b.severity
        );

        const result = textA < textB ? -1 : textA > textB ? 1 : 0;
        return direction === 'asc' ? result : -result;
      });
    } else {
      this.table.dataSource.sort((a, b) => {
        var textA = a[name as keyof Finding]?.toString() ?? '';
        var textB = b[name as keyof Finding]?.toString() ?? '';
        const result = textA < textB ? -1 : textA > textB ? 1 : 0;
        return direction === 'asc' ? result : -result;
      });
    }
    this.table.dataSource = [...this.table.dataSource];
  }
}

Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Resolved

Specification of the issue
  • User: Premium
  • Premium support: Yes
  • Technology: MDB Angular
  • MDB Version: MDB5 2.0.0
  • Device: any
  • Browser: any
  • OS: any
  • Provided sample code: No
  • Provided link: Yes