Angular Data GridRows sorting

Sort data alphabetically or numerically, in ascending, descending or a custom order, by one or multiple columns.

Overview

With sorting, you can easily rearrange rows of data, based on the values in specific columns. This is particularly useful for analyzing and organizing large data sets, which helps you identify patterns and trends.

You can sort data in different ways:

  • Alphabetically, numerically, or based on a custom sorting logic
  • In ascending, descending, or a custom order
  • By a single column, or by multiple columns
  • Using Handsontable's UI or API

Handsontable sorts data only visually, so your source data remains in the original order. To save your sorting changes in the data source, see this guide: Saving data.

Sorting demo

Click on one of the column names to sort the values in ascending (↑) or descending (↓) order, or to go back to the original order.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from "@handsontable/angular-wrapper";

@Component({
  selector: 'app-example1',
  template: `
    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  standalone: false
})
export class AppComponent {

  readonly hotData = [
    {
      brand: 'Jetpulse',
      model: 'Racing Socks',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
    },
    {
      brand: 'Gigabox',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
    },
    {
      brand: 'Camido',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
    },
    {
      brand: 'Eidel',
      model: 'HL Road Tire',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
    },
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        title: 'Brand',
        type: 'text',
        data: 'brand',
      },
      {
        title: 'Model',
        type: 'text',
        data: 'model',
      },
      {
        title: 'Price',
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        title: 'Date',
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time',
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'In stock',
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    // enable sorting for all columns
    columnSorting: true,
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

Enable sorting

To enable sorting for all columns, set columnSorting to true.

import {GridSettings, HotTableModule} from '@handsontable/angular-wrapper';

const configurationOptions: GridSettings = {
  // enable sorting for all columns
  columnSorting: true,
};
<hot-table [settings]="configurationOptions"></hot-table>

To enable sorting only for specific columns, set headerAction to false for those columns that you don't want to sort. In the following example, only columns Model, Date and In stock are sortable.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from "@handsontable/angular-wrapper";

@Component({
  selector: 'app-example2',
  template: `
    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  standalone: false
})
export class AppComponent {

  readonly hotData = [
    {
      brand: 'Jetpulse',
      model: 'Racing Socks',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
    },
    {
      brand: 'Gigabox',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
    },
    {
      brand: 'Camido',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
    },
    {
      brand: 'Eidel',
      model: 'HL Road Tire',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
    },
  ];

  readonly hotSettings: GridSettings = {
// enable sorting for all columns
    columnSorting: true,
    columns: [
      {
        title: 'Brand',
        type: 'text',
        data: 'brand',
        // disable sorting for the 'Brand' column
        columnSorting: {
          headerAction: false,
        },
      },
      {
        title: 'Model',
        type: 'text',
        data: 'model',
      },
      {
        title: 'Price',
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
        // disable sorting for the 'Price' column
        columnSorting: {
          headerAction: false,
        },
      },
      {
        title: 'Date',
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time',
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
        // disable sorting for the 'Time' column
        columnSorting: {
          headerAction: false,
        },
      },
      {
        title: 'In stock',
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

Configure sorting

You can configure the sorting UI, set an initial sort order, and implement your own comparator.

By default:

  • Sorting is enabled for all columns.
  • The end user can sort data by clicking on the column name.
  • The sort order indicator is visible.
  • At Handsontable's initialization, no rows are sorted.

You can configure the following options:

import {GridSettings, HotTableModule} from '@handsontable/angular-wrapper';

const configurationOptions: GridSettings = {
  columnSorting: {
    // let the end user sort data by clicking on the column name (set by default)
    headerAction: true,
    // don't sort empty cells – move rows that contain empty cells to the bottom (set by default)
    sortEmptyCells: false,
    // enable the sort order icon that appears next to the column name (set by default)
    indicator: true,

    // at initialization, sort data by the first column, in descending order
    initialConfig: {
      column: 1,
      sortOrder: 'desc',
    },

    // implement your own comparator
    compareFunctionFactory(sortOrder, columnMeta) {
      return function (value, nextValue) {
        // here, add a compare function
        // that returns `-1`, or `0`, or `1`
      };
    },
  },
};
<hot-table [settings]="configurationOptions"></hot-table>

Sort different types of data

Handsontable sorts different types of data automatically, based on which type you configure for each column.

You can configure the following types:

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from "@handsontable/angular-wrapper";

@Component({
  selector: 'app-example3',
  template: `
    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  standalone: false
})
export class AppComponent {

  readonly hotData = [
    {
      model: 'Racing Socks',
      size: 'S',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
      color: 'Black',
      email: '8576@all.xyz',
    },
    {
      model: 'HL Mountain Shirt',
      size: 'XS',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
      color: 'White',
      email: 'tayn@all.xyz',
    },
    {
      model: 'Cycling Cap',
      size: 'L',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
      color: 'Green',
      email: '6lights@far.com',
    },
    {
      model: 'Ski Jacket',
      size: 'M',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
      color: 'Blue',
      email: 'raj@fq1my2c.com',
    },
    {
      model: 'HL Goggles',
      size: 'XL',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
      color: 'Black',
      email: 'da@pdc.ga',
    },
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        title: 'Model<br>(text)',
        // set the type of the 'Model' column
        type: 'text',
        data: 'model',
      },
      {
        title: 'Price<br>(numeric)',
        // set the type of the 'Price' column
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        title: 'Sold on<br>(date)',
        // set the type of the 'Date' column
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time<br>(time)',
        // set the type of the 'Time' column
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'In stock<br>(checkbox)',
        // set the type of the 'In stock' column
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
      {
        title: 'Size<br>(dropdown)',
        // set the type of the 'Size' column
        type: 'dropdown',
        data: 'size',
        source: ['XS', 'S', 'M', 'L', 'XL'],
        className: 'htCenter',
      },
      {
        title: 'Color<br>(autocomplete)',
        // set the type of the 'Size' column
        type: 'autocomplete',
        data: 'color',
        source: ['White', 'Black', 'Yellow', 'Blue', 'Green'],
        className: 'htCenter',
      },
      {
        title: 'Email<br>(password)',
        // set the type of the 'Email' column
        type: 'password',
        data: 'email',
      },
    ],
    columnSorting: true,
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

You can also create a custom type. For details, see this guide: Cell type.

Sort by multiple columns

You can sort data by more than one column, which lets you apply multiple sets of sort criteria at the same time.

To try out sorting by multiple columns, see the following demo:

  1. Click on the Brand column name. The data gets sorted by brand.
  2. Hold down Cmd/Ctrl and click on the Model column name.
    The data gets sorted by model, but within each brand.
  3. Hold down Cmd/Ctrl and click on the Price column name.
    The data gets sorted by price, but within each model.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from "@handsontable/angular-wrapper";

@Component({
  selector: 'app-example4',
  template: `
    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  standalone: false
})
export class AppComponent {

  readonly hotData = [
    {
      brand: 'Jetpulse',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
    },
    {
      brand: 'Jetpulse',
      model: 'HL Mountain Frame',
      price: 30,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
    },
    {
      brand: 'Jetpulse',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 279.99,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'HL Road Tire',
      price: 59,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
    },
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        title: 'Brand',
        type: 'text',
        data: 'brand',
      },
      {
        title: 'Model',
        type: 'text',
        data: 'model',
      },
      {
        title: 'Price',
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        title: 'Date',
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time',
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'In stock',
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    // enable sorting by multiple columns, for all columns
    multiColumnSorting: true,
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

To enable sorting by multiple columns, set multiColumnSorting to true.

import {GridSettings, HotTableModule} from '@handsontable/angular-wrapper';

const configurationOptions: GridSettings = {
  // enable sorting by multiple columns, for all columns
  multiColumnSorting: true,
};
<hot-table [settings]="configurationOptions"></hot-table>

To select which columns can be sorted at the same time, set headerAction to false for those columns that you don't want to sort.

import {GridSettings, HotTableModule} from '@handsontable/angular-wrapper';

const configurationOptions: GridSettings = {
  // enable sorting by multiple columns, for all columns
  multiColumnSorting: true,
  columns: [
    {
      // disable sorting by multiple columns for the first column
      multiColumnSorting: {
        headerAction: false,
      },
    },
};
<hot-table [settings]="configurationOptions"></hot-table>

The columnSorting and multiColumnSorting options are mutually exclusive; do not enable them together. If you do, columnSorting will be automatically disabled as it is overridden by multiColumnSorting.

Set an initial sort order

You can set a default sort order that's applied every time you initialize Handsontable.

In the following demo, the data is initially sorted:

  • By the Brand column, in ascending order
  • By the Model column, in descending order

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from "@handsontable/angular-wrapper";

@Component({
  selector: 'app-example5',
  template: `
    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  standalone: false
})
export class AppComponent {

  readonly hotData = [
    {
      brand: 'Jetpulse',
      model: 'HL Mountain Frame',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
    },
    {
      brand: 'Jetpulse',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
    },
    {
      brand: 'Jetpulse',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'HL Road Tire',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
    },
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        title: 'Brand',
        type: 'text',
        data: 'brand',
      },
      {
        title: 'Model',
        type: 'text',
        data: 'model',
      },
      {
        title: 'Price',
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        title: 'Date',
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time',
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'In stock',
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    multiColumnSorting: {
      initialConfig: [
        // at initialization, sort the data by the 'Brand' column, in ascending order
        {
          column: 0,
          sortOrder: 'asc',
        },
        // at initialization, sort the data by the 'Model' column, in descending order
        {
          column: 1,
          sortOrder: 'desc',
        },
      ],
    },
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

To set an initial sort order, use the initialConfig option.

import {GridSettings, HotTableModule} from '@handsontable/angular-wrapper';

const configurationOptions: GridSettings = {
  columnSorting: {
    // at initialization, sort data by the first column, in ascending order
    initialConfig: {
      column: 0,
      sortOrder: 'asc', // for descending order, use `'desc'`
    },
};
<hot-table [settings]="configurationOptions"></hot-table>

To initially sort data by multiple columns, set initialConfig to an array.

import {GridSettings, HotTableModule} from '@handsontable/angular-wrapper';

const configurationOptions: GridSettings = {
  // enable sorting by multiple columns
  multiColumnSorting: {
    initialConfig: [
      // at initialization, sort data by the first column, in ascending order
      {
        column: 0,
        sortOrder: 'asc',
      },
      // at initialization, sort data by the second column, in descending order
      {
        column: 1,
        sortOrder: 'desc',
      },
    ]
};
<hot-table [settings]="configurationOptions"></hot-table>

Add custom sort icons

The default sort icons (↑↓) are encoded url. You can replace them by changing -webkit-mask-image for the following pseudo-elements of Handsontable's CSS:

  • .columnSorting.sortAction.ascending::before
  • .columnSorting.sortAction.descending::before

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from "@handsontable/angular-wrapper";

@Component({
  selector: 'app-example6',
  template: `
    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  styles: `
    :host ::ng-deep {
      .custom-sort-icon-example-1 .columnSorting.sortAction.ascending::before {
        -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 0h24v24H0z' stroke='none'/%3E%3Cpath d='M12 21V9M8 13l4-4 4 4'/%3E%3Cpath d='M21 12a9 9 0 0 0-18 0'/%3E%3C/svg%3E");
      }

      .custom-sort-icon-example-1 .columnSorting.sortAction.descending::before {
        -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 0h24v24H0z' stroke='none'/%3E%3Cpath d='M12 3v12M16 11l-4 4-4-4'/%3E%3Cpath d='M3 12a9 9 0 0 0 18 0'/%3E%3C/svg%3E");
      }
    }
  `,
  standalone: false
})
export class AppComponent {

  readonly hotData = [
    {
      brand: 'Jetpulse',
      model: 'Racing Socks',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
    },
    {
      brand: 'Gigabox',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
    },
    {
      brand: 'Camido',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
    },
    {
      brand: 'Eidel',
      model: 'HL Road Tire',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
    },
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        title: 'Brand',
        type: 'text',
        data: 'brand',
      },
      {
        title: 'Model',
        type: 'text',
        data: 'model',
      },
      {
        title: 'Price',
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        title: 'Date',
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time',
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'In stock',
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    className: 'custom-sort-icon-example-1',
    columnSorting: {
      initialConfig: {
        column: 1,
        sortOrder: 'desc',
      },
    },
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

To replace the icons that indicate sorting by multiple columns (1, 2 etc.), change content for the .columnSorting.sort-1::after and subsequent pseudo-elements:

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from "@handsontable/angular-wrapper";

@Component({
  selector: 'app-example7',
  template: `
    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  styles: `
    :host ::ng-deep {
      .custom-sort-icon-example-3 .handsontable span.colHeader.columnSorting.sort-1::after {
        content: '①';
      }

      .custom-sort-icon-example-3 .handsontable span.colHeader.columnSorting.sort-2::after {
        content: '②';
      }

      .custom-sort-icon-example-3 .handsontable span.colHeader.columnSorting.sort-3::after {
        content: '③';
      }

      .custom-sort-icon-example-3 .handsontable span.colHeader.columnSorting.sort-4::after {
        content: '④';
      }

      .custom-sort-icon-example-3 .handsontable span.colHeader.columnSorting.sort-5::after {
        content: '⑤';
      }

      .custom-sort-icon-example-3 .handsontable span.colHeader.columnSorting.sort-6::after {
        content: '⑥';
      }

      .custom-sort-icon-example-3 .handsontable span.colHeader.columnSorting.sort-7::after {
        content: '⑦';
      }

      .custom-sort-icon-example-3 .handsontable span.colHeader.columnSorting::after {
        width: 10px;
        font-size: 10px;
      }

      .custom-sort-icon-example-3 .handsontable .columnSorting.sortAction:before {
        right: 5px;
      }
    }
  `,
  standalone: false
})
export class AppComponent {

  readonly hotData = [
    {
      brand: 'Jetpulse',
      model: 'Racing Socks',
      color: 'White',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
    },
    {
      brand: 'Gigabox',
      model: 'HL Frame',
      color: 'Black',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
    },
    {
      brand: 'Camido',
      model: 'Cycling Cap',
      color: 'Red',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      color: 'Green',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
    },
    {
      brand: 'Eidel',
      model: 'HL Road Tire',
      color: 'Blue',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
    },
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        title: 'Brand',
        type: 'text',
        data: 'brand',
      },
      {
        title: 'Model',
        type: 'text',
        data: 'model',
      },
      {
        title: 'Color',
        type: 'text',
        data: 'color',
      },
      {
        title: 'Price',
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        title: 'Date',
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time',
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'In stock',
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    className: 'custom-sort-icon-example-3',
    multiColumnSorting: {
      initialConfig: [
        {
          column: 0,
          sortOrder: 'asc',
        },
        {
          column: 1,
          sortOrder: 'desc',
        },
        {
          column: 2,
          sortOrder: 'asc',
        },
        {
          column: 3,
          sortOrder: 'desc',
        },
        {
          column: 4,
          sortOrder: 'asc',
        },
        {
          column: 5,
          sortOrder: 'desc',
        },
        {
          column: 6,
          sortOrder: 'asc',
        },
      ],
    },
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

Add a custom comparator

A comparator is a function that determines the sort order, based on specified criteria.

Adding a custom comparator lets you go beyond Handsontable's built-in sorting features. You can:

  • Apply a custom sort order. For example, instead of sorting data alphabetically or numerically, you can sort it by length or by the occurrence of a specific character.
  • Handle exceptions. For example, in a list of employees, you can exclude workers with a specific job title from sorting.
  • Implement a custom sorting logic based on your own criteria.

To add a custom comparator, use the compareFunctionFactory option.

import {GridSettings, HotTableModule} from '@handsontable/angular-wrapper';

const configurationOptions: GridSettings = {
  columnSorting: {
    compareFunctionFactory: function (sortOrder, columnMeta) {
      // implement your own comparator
      return function (value, nextValue) {
        if (value < nextValue) {
          return -1;
        }
        if (value > nextValue) {
          return 1;
        }

        return 0;
      };
    },
  },
};
<hot-table [settings]="configurationOptions"></hot-table>

Use sorting hooks

You can run your code before or after sorting, using the following Handsontable hooks:

For example, you can use beforeColumnSort() for server-side sorting, or use afterColumnSort() to exclude rows from sorting.

import {GridSettings, HotTableModule} from '@handsontable/angular-wrapper';

const configurationOptions: GridSettings = {
  beforeColumnSort() {
    // add your code here
    return false; // to block front-end sorting
  },
  afterColumnSort() {
    // add your code here
  },
};
<hot-table [settings]="configurationOptions"></hot-table>

Exclude rows from sorting

You can exclude any number of top or bottom rows from sorting.

For example, if you freeze a row at the top (to display column names), and freeze a row at the bottom (to display column summaries), you can prevent those frozen rows from getting sorted, so they always stay in place.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from "@handsontable/angular-wrapper";

@Component({
  selector: 'app-example8',
  template: `
    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  standalone: false
})
export class AppComponent {

  readonly hotData = [
    {
      brand: 'Brand',
      model: 'Model',
      price: 'Price',
      sellDate: 'Date',
      sellTime: 'Time',
      inStock: 'In stock',
    },
    {
      brand: 'Gigabox',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: 11,
    },
    {
      brand: 'Camido',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: 0,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: 1,
    },
    {
      brand: 'Eidel',
      model: 'HL Road Tire',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: 3,
    },
    {
      brand: 'Jetpulse',
      model: 'Racing Socks',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: 5,
    },
    {
      brand: 'Gigabox',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: 22,
    },
    {
      brand: 'Camido',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: 13,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: 0,
    },
    {
      brand: 'Eidel',
      model: 'HL Road Tire',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: 14,
    },
    {
      brand: 'Jetpulse',
      model: 'Racing Socks',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: 16,
    },
    {
      brand: 'Gigabox',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: 18,
    },
    {
      brand: 'Camido',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: 3,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: 0,
    },
    {
      brand: 'Vinte',
      model: 'ML Road Frame-W',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: 2,
    },
    {},
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        type: 'text',
        data: 'brand',
      },
      {
        type: 'text',
        data: 'model',
      },
      {
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        type: 'numeric',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    height: 200,
    stretchH: 'all',
    fixedRowsTop: 1,
    fixedRowsBottom: 1,
    colHeaders: true,
    columnSorting: true,
    // `afterColumnSort()` is a Handsontable hook: it's fired after each sorting
    afterColumnSort() {

      // @ts-ignore
      const lastRowIndex = hot.countRows() - 1;

      // after each sorting, take row 1 and change its index to 0
      // @ts-ignore
      hot.rowIndexMapper.moveIndexes(hot.toVisualRow(0), 0);
      // after each sorting, take row 16 and change its index to 15
      // @ts-ignore
      hot.rowIndexMapper.moveIndexes(hot.toVisualRow(lastRowIndex), lastRowIndex);
    },
    cells(row) {
      const lastRowIndex = this.instance.countRows() - 1;

      if (row === 0) {
        return {
          type: 'text',
          className: 'htCenter',
          readOnly: true,
        };
      }

      if (row === lastRowIndex) {
        return {
          type: 'numeric',
          className: 'htCenter',
        };
      }

      return {
        type: 'text',
      };
    },
    columnSummary: [
      {
        sourceColumn: 2,
        type: 'sum',
        reversedRowCoords: true,
        destinationRow: 0,
        destinationColumn: 2,
        forceNumeric: true,
        suppressDataTypeErrors: true,
      },
      {
        sourceColumn: 5,
        type: 'sum',
        reversedRowCoords: true,
        destinationRow: 0,
        destinationColumn: 5,
        forceNumeric: true,
        suppressDataTypeErrors: true,
      },
    ],
    autoWrapRow: true,
    autoWrapCol: true,
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

Control sorting programmatically

You can control sorting at the grid's runtime by using Handsontable's hooks and API methods.

This allows you to:

  • Enable or disable sorting depending on specified conditions. For example, you can disable sorting for very large data sets.
  • Trigger sorting depending on the state of another component in your application. For example, you can let the end user sort data by clicking on buttons outside of the grid.

Enable or disable sorting programmatically

To enable or disable sorting programmatically, use the updateSettings() method.

// enable sorting for all columns
handsontableInstance.updateSettings({
  columnSorting: true,
});

// disable sorting for all columns
handsontableInstance.updateSettings({
  columnSorting: false,
});
@ViewChild(HotTableComponent, {static: false})
hotTable!: HotTableComponent;

ngAfterViewInit() {
  const hot = this.hotTable.hotInstance;

  // enable sorting for all columns
  hot.updateSettings({
    columnSorting: true,
  });

  // disable sorting for all columns
  hot.updateSettings({
    columnSorting: false,
  });
}

You can also enable or disable sorting for specific columns.

@ViewChild(HotTableComponent, {static: false})
hotTable!: HotTableComponent;

ngAfterViewInit() {
  const hot = this.hotTable.hotInstance;

  hot.updateSettings({
    columns: [
      {
        // enable sorting for the first column
        columnSorting: {
          headerAction: true,
        },
      },
      {
        // disable sorting for the second column
        columnSorting: {
          headerAction: false,
        },
      },
    ],
  });
}

Sort data programmatically

To sort data programmatically, use the columnSorting.sort() method. Remember to enable sorting first.

Mind that calling columnSorting.sort() overwrites any previous sort orders.

@ViewChild(HotTableComponent, {static: false})
hotTable!: HotTableComponent;

configurationOptions: GridSettings = {
  // enable sorting for all columns
  columnSorting: true,
};

ngAfterViewInit() {
  const hot = this.hotTable.hotInstance;

  const columnSorting = hot.getPlugin('columnSorting');

  columnSorting.sort(
    // sort data by the first column, in ascending order
    {
      column: 0,
      sortOrder: 'asc', // for descending order, use `'desc'`
    }
  );

  // go back to the original order
  columnSorting.clearSort();
}
<hot-table [settings]="configurationOptions"></hot-table>

To see how it works, try out the following demo:

/* file: app.component.ts */
import {Component, ViewChild} from '@angular/core';
import {GridSettings, HotTableComponent } from '@handsontable/angular-wrapper';
import {ColumnSorting} from 'handsontable/plugins';

@Component({
  selector: 'app-example9',
  template: `
    <div class="controls">
      <button (click)="sortAscBrand()" class="button">Sort by the "Brand" column, in ascending order</button>
      <button (click)="unsort()" class="button">Go back to the original order</button>
    </div>

    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  standalone: false
})
export class AppComponent {
  @ViewChild(HotTableComponent, {static: false}) hotTable!: HotTableComponent;

  readonly hotData = [
    {
      brand: 'Jetpulse',
      model: 'Racing Socks',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
    },
    {
      brand: 'Gigabox',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
    },
    {
      brand: 'Camido',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
    },
    {
      brand: 'Eidel',
      model: 'HL Road Tire',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
    },
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        title: 'Brand',
        type: 'text',
        data: 'brand',
      },
      {
        title: 'Model',
        type: 'text',
        data: 'model',
      },
      {
        title: 'Price',
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        title: 'Date',
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time',
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'In stock',
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    columnSorting: true,
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };

  getColumnSortingPlugin(): ColumnSorting {
    return this.hotTable.hotInstance!.getPlugin('columnSorting')
  }

  sortAscBrand() {
    this.getColumnSortingPlugin().sort({
      column: 0,
      sortOrder: 'asc',
    });
  }

  unsort() {
    this.getColumnSortingPlugin().clearSort();
  }
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

Sort data programmatically by multiple columns

To sort data programmatically by multiple columns, use the multiColumnSorting.sort() method. Remember to enable sorting by multiple columns first.

Mind that calling multiColumnSorting.sort() overwrites any previous sort orders.

@ViewChild(HotTableComponent, {static: false})
hotTable!: HotTableComponent;

configurationOptions: GridSettings = {
  // enable sorting for all columns
  multiColumnSorting: true,
};

ngAfterViewInit() {
  const hot = this.hotTable.hotInstance;

  // get the `MultiColumnSorting` plugin
  const multiColumnSorting = hot.getPlugin('multiColumnSorting');

  multiColumnSorting.sort([
    // sort data by the first column, in ascending order
    {
      column: 0,
      sortOrder: 'asc',
    },
    // within the above sort criteria,
    // sort data by the second column, in descending order
    {
      column: 1,
      sortOrder: 'desc',
    },
  ]);

  // go back to the original order
  multiColumnSorting.clearSort();
}
<hot-table [settings]="configurationOptions"></hot-table>

To see how it works, try out the following demo:

/* file: app.component.ts */
import { Component, ViewChild } from '@angular/core';
import { GridSettings, HotTableComponent } from '@handsontable/angular-wrapper';
import { ColumnSorting } from 'handsontable/plugins';

@Component({
  selector: 'app-example10',
  template: `
    <div class="example-controls-container">
      <div class="controls">
        <button (click)="sort()" class="button">Sort</button>
      </div>
    </div>

    <hot-table
      [settings]="hotSettings!" [data]="hotData">
    </hot-table>
  `,
  standalone: false
})
export class AppComponent {
  @ViewChild(HotTableComponent, {static: false}) hotTable!: HotTableComponent;

  readonly hotData = [
    {
      brand: 'Jetpulse',
      model: 'Racing Socks',
      price: 30,
      sellDate: 'Oct 11, 2023',
      sellTime: '01:23 AM',
      inStock: false,
    },
    {
      brand: 'Jetpulse',
      model: 'HL Mountain Frame',
      price: 1890.9,
      sellDate: 'May 3, 2023',
      sellTime: '11:27 AM',
      inStock: false,
    },
    {
      brand: 'Jetpulse',
      model: 'Cycling Cap',
      price: 130.1,
      sellDate: 'Mar 27, 2023',
      sellTime: '03:17 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'Road Tire Tube',
      price: 59,
      sellDate: 'Aug 28, 2023',
      sellTime: '08:01 AM',
      inStock: true,
    },
    {
      brand: 'Chatterpoint',
      model: 'HL Road Tire',
      price: 279.99,
      sellDate: 'Oct 2, 2023',
      sellTime: '13:23 AM',
      inStock: true,
    },
  ];

  readonly hotSettings: GridSettings = {
    columns: [
      {
        title: 'Brand',
        type: 'text',
        data: 'brand',
      },
      {
        title: 'Model',
        type: 'text',
        data: 'model',
      },
      {
        title: 'Price',
        type: 'numeric',
        data: 'price',
        numericFormat: {
          pattern: '$ 0,0.00',
          culture: 'en-US',
        },
      },
      {
        title: 'Date',
        type: 'date',
        data: 'sellDate',
        dateFormat: 'MMM D, YYYY',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'Time',
        type: 'time',
        data: 'sellTime',
        timeFormat: 'hh:mm A',
        correctFormat: true,
        className: 'htRight',
      },
      {
        title: 'In stock',
        type: 'checkbox',
        data: 'inStock',
        className: 'htCenter',
      },
    ],
    multiColumnSorting: true,
    height: 'auto',
    stretchH: 'all',
    autoWrapRow: true,
    autoWrapCol: true,
  };

  getMultiColumnSortingPlugin(): ColumnSorting {
    return this.hotTable.hotInstance!.getPlugin('multiColumnSorting')
  }

  sort() {
    this.getMultiColumnSortingPlugin().sort([
      {
        column: 0,
        sortOrder: 'asc',
      },
      {
        column: 1,
        sortOrder: 'desc',
      },
    ]);
  }
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';

import { AppComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ AppComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ AppComponent ]
})

export class AppModule { }

Import the sorting module

You can reduce the size of your bundle by importing and registering only the modules that you need.

To use sorting, you need only the following modules:

// import the base module
import Handsontable from 'handsontable/base';

// import Handsontable's CSS
import 'handsontable/styles/handsontable.css';
import 'handsontable/styles/ht-theme-main.css';

// import the ColumnSorting plugin (or the MultiColumnSorting plugin)
import { registerPlugin, ColumnSorting } from 'handsontable/plugins';

// register the ColumnSorting (or MultiColumnSorting plugin)
registerPlugin(ColumnSorting);
Windows macOS Action Excel Sheets
Enter Enter Sort data by the selected column, in ascending, descending, or the original order
Ctrl+Enter Cmd+Enter Sort data by multiple columns, in ascending, descending, or the original order. Requires the MultiColumnSorting plugin.

API reference

For the list of options, methods, and Handsontable hooks related to sorting, see the following API reference pages:

Troubleshooting

Didn't find what you need? Try this: