Sorting Data

SpreadJS can sort data in either ascending or descending order. This can be accessed within the spreadsheet interface via table headers or context menus on cell range selections. JavaScript code can also be used for sorting by using the sheet's sortRange function. In some cases, traditional ascending and descending sorting is not enough, which is why sorting on the workbook can be customized with compare functions as described in the code below and sort by color.

Use the sortRange method to sort a range with given order, as shown in the following code. Users can also use context menu -> sort or filter to sort the data. The priority of data type is: boolean > string > number, eg: TRUE > '4' > 8. Customize sorting User can use the callback function to define the customized compare method to sort. The following code shows use localCompare to sort. Sort By Cell Color User can sort cell by cell color according special sort info. This feature supports the solid fill, pattern fill, gradient fill. The following code shows use backColor to sort by cell color. Sort by Font Color User can sort cell by font color according the special sort info. This feature only supports solid fill. User can define the callback function to used when the RangeSorting events raised. Group sort You can set the whether keep the data grouped by using the groupSort options. The GroupSort provides the following types: flat: Sort ignore the group. group: Move the group with the sort, but don't sort inner the group child: Only sort inner the group. full: Move the group with the sort, and sort inner the group The following code shows use groupSort option. User can define the groupSort option to used when the RangeSorting events raised. Sort ignore hidden You can set the whether ignore the hidden values when sorting. When ignoreHidden set to true, spread will skip and don't move the hidden value. When ignoreHidden set to false, spread will compare and move the hidden value. When groupSort set to group/child/full, SpreadJS will move the hidden value and move the row/column visibility to keep value hidden. The following code shows use ignoreHidden option. User can define the ignoreHidden option to used when the RangeSorting events raised. By default, if the sort ranges contains group, SpreadJS will sort the data with option group sort. Otherwise, it will use flat sort and ignore hidden. Keep last sort state The Last sort Action setting will be recorded by worksheet, you can get the last sort state by using getSortState function. The following code shows use getSortState function. And now you can call the sortRange without passing any arguments to trigger reorder, the reorder action are base on the last sort state. User can use sortRange with bind function to implement automatic sorting.The following code shows a simple code example. The above code will be called automatically when a value in the sheet changed, so a reorder will be triggered whenever you make a value change to the sheet.
import { Component, NgModule, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { SpreadSheetsModule } from '@mescius/spread-sheets-angular'; import GC from '@mescius/spread-sheets'; import './styles.css'; const spreadNS = GC.Spread.Sheets, SheetArea = spreadNS.SheetArea; const CELL_COLOR_MAPPING = { red: "#FF0000", green: "#00B050", blue: "#00B0F0", gradient: { degree: 90, stops: [ { color: "#ffffff", position: 0, }, { color: "#5B9BD5", position: 1, } ] }, pattern: { patternColor: "", type: 14, backgroundColor: "" } } const FONT_COLOR_MAPPING = { red: "#FF0000", blue: "#00B0F0", purple: "#7030A0", green: "#92D050", null: "" } @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { spread: GC.Spread.Sheets.Workbook; hostStyle = { width: 'calc(100% - 280px)', height: '100%', overflow: 'hidden', float: 'left' }; constructor() { } initSpread($event: any) { let spread = this.spread = $event.spread; spread.suspendPaint(); spread.fromJSON(sjsData); this.initSheet0(spread.getSheet(0)); this.initSheet1(spread.getSheet(1)); this.initSheet2(spread.getSheet(2)); this.initSheet3(spread.getSheet(3)); this.initSheet4(spread.getSheet(4)); this.initSheet5(spread.getSheet(5)); this.initSheet6(spread.getSheet(6)); this.initSheet7(spread.getSheet(7)); this.initSortStatePanel(spread); spread.resumePaint(); } initSheet0(sheet: GC.Spread.Sheets.Worksheet) { var style = sheet.getStyle(4, 7); style.cellButtons = [{ useButtonStyle: true, caption: "Sort by last name", width: 222, command: function () { sheet.sortRange(4, 0, 27, 5, true, [ { index: 1, ascending: true, compareFunction: function (value1, value2) { var str1 = value1.split(" ")[1], str2 = value2.split(" ")[1]; return str1.localeCompare(str2); } }, ]) }, }]; sheet.setStyle(4, 7, style); var grade = ["Freshmen", "Sophomore", "Junior", "Senior"]; var clothesSize = ["XX-Small", "X-Small", "Small", "Medium", "Large", "X-Large", "XX-Large"]; function compareList (obj1: Object, obj2: Object, list: any[]) { var index1 = list.indexOf(obj1), index2 = list.indexOf(obj2); if (index1 > index2) { return 1; } else if (index1 < index2) { return -1; } else { return 0; } } style = sheet.getStyle(5, 7); style.cellButtons = [{ useButtonStyle: true, caption: "Sort by Grade", width: 222, command: function () { sheet.sortRange(4, 0, 27, 5, true, [ { index: 2, ascending: true, compareFunction: function (value1, value2) { return compareList(value1, value2, grade); } }, ]) }, }]; sheet.setStyle(5, 7, style); style = sheet.getStyle(6, 7); style.cellButtons = [{ useButtonStyle: true, caption: "Sort by T-Shirt Size", width: 222, command: function () { sheet.sortRange(4, 0, 27, 5, true, [ { index: 3, ascending: true, compareFunction: function (value1, value2) { return compareList(value1, value2, clothesSize); } }, ]) }, }]; sheet.setStyle(6, 7, style); } initSheet1 (sheet: GC.Spread.Sheets.Worksheet) { function sortDomain (value1: string, value2: string) { var str1 = value1.substr(value1.lastIndexOf(".") + 1), str2 = value2.substr(value2.lastIndexOf(".") + 1); return str1.localeCompare(str2); } function sortIP (ip1: string, ip2: string) { var value1 = ip1.split("."), value2 = ip2.split("."); for (var i = 0; i < 4; i++) { var num1 = parseInt(value1[i]), num2 = parseInt(value2[i]); if (num1 > num2) { return 1; } else if (num1 < num2) { return -1; } } return 0; } sheet.bind(GC.Spread.Sheets.Events.RangeSorting, function (e, info) { if (info.col === 0) { info.compareFunction = sortDomain; } else if (info.col === 1) { info.compareFunction = sortIP; } }); } initSheet2 (sheet: GC.Spread.Sheets.Worksheet) { sheet.bind(GC.Spread.Sheets.Events.RangeSorting, function (e, info) { info.groupSort = GC.Spread.Sheets.GroupSort.full; }); } initSheet3 (sheet: GC.Spread.Sheets.Worksheet) { sheet.outlineColumn.options({ columnIndex: 0, showImage: false, showIndicator: true, showCheckBox: true, maxLevel: 10 }); } initSheet4 (sheet: GC.Spread.Sheets.Worksheet) { var style = sheet.getStyle(1, 4); style.cellButtons = [ { useButtonStyle: true, caption: "ignoreHidden = true", command: function () { sheet.sortRange(2, 0, 15, 1, true, [ { index: 0, ascending: sheet.getValue(1, 3) === '1', }, ], { ignoreHidden: true }); }, }, { useButtonStyle: true, caption: "ignoreHidden = false", command: function () { sheet.sortRange(2, 0, 15, 1, true, [ { index: 0, ascending: sheet.getValue(1, 3) === '1', }, ], { ignoreHidden: false }); }, }, { useButtonStyle: true, caption: "groupSort = group", command: function () { sheet.sortRange(2, 0, 15, 1, true, [ { index: 0, ascending: sheet.getValue(1, 3) === '1', }, ], { groupSort: GC.Spread.Sheets.GroupSort.group }); }, }]; sheet.setStyle(1, 4, style); } initSheet5 (sheet: GC.Spread.Sheets.Worksheet) { sheet.setColumnWidth(4, 120); var style = new GC.Spread.Sheets.Style(); style.cellButtons = [{ caption: "Sort By Cell Color", useButtonStyle: true, width: 120, command: function (sheet: GC.Spread.Sheets.Worksheet) { var value = sheet.getValue(15, 3); var order = sheet.getValue(15, 4); value = value ? value : "red"; order = order ? order : "top"; var color = CELL_COLOR_MAPPING[value]; sheet.sortRange(3, 2, 10, 1, true, [{ index: 2, backColor: color, order: order, }]) } }]; sheet.setStyle(16, 4, style); } initSheet6 (sheet: GC.Spread.Sheets.Worksheet) { sheet.setColumnWidth(4, 120); var style = new GC.Spread.Sheets.Style(); style.cellButtons = [{ caption: "Sort By Font Color", useButtonStyle: true, width: 120, command: function (sheet: GC.Spread.Sheets.Worksheet) { var value = sheet.getValue(15, 3); var order = sheet.getValue(15, 4); value = value ? value : "red"; order = order ? order : "top"; var color = FONT_COLOR_MAPPING[value]; sheet.sortRange(3, 2, 10, 1, true, [{ index: 2, fontColor: color, order: order }]) } }]; sheet.setStyle(16, 4, style); } initSheet7(sheet: GC.Spread.Sheets.Worksheet) { function inSortStateRange(sortState : any, row: number, col: number) { if (row >= sortState.row && row < sortState.row + sortState.rowCount && col >= sortState.col && col < sortState.col + sortState.colCount) { return true; } return false; } sheet.sortRange(2, 2, 10, 1, true, [{ index: 2, ascending: false, compareFunction: undefined }]); sheet.setSelection(2, 2, 10, 1); sheet.bind(GC.Spread.Sheets.Events.ValueChanged, function(e, info) { let sortState = sheet.getSortState(); if (inSortStateRange(sortState, info.row, info.col)) { sheet.sortRange(); } }); } initSortStatePanel(spread: GC.Spread.Sheets.Workbook) { this._getElementById('get_SortState_Btn').addEventListener('click', function() { let sheet = spread.getActiveSheet(); let sortState = sheet.getSortState(); if (!sortState) { return; } let { row, col, rowCount, colCount, byRow, sortConditions } = sortState; if (sortState) { let sortStateStr = ''; sortStateStr += "row: " + row + ",\n"; sortStateStr += "col: " + col + ",\n"; sortStateStr += "rowCount: " + rowCount + ",\n"; sortStateStr += "colCount: " + colCount + ",\n"; sortStateStr += "byRow: " + byRow + ",\n"; sortStateStr += "sortCondition: " + JSON.stringify(sortConditions); +"}\n"; document.getElementById("showEventArgs").value = sortStateStr; } }); } _getElementById(id:string) { return document.getElementById(id); } } @NgModule({ imports: [BrowserModule, SpreadSheetsModule, FormsModule], declarations: [AppComponent], exports: [AppComponent], bootstrap: [AppComponent] }) export class AppModule {} enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule);
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/angular/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/spread/source/data/sorting.js" type="text/javascript"></script> <!-- Polyfills --> <script src="$DEMOROOT$/en/angular/node_modules/core-js/client/shim.min.js"></script> <script src="$DEMOROOT$/en/angular/node_modules/zone.js/fesm2015/zone.min.js"></script> <!-- SystemJS --> <script src="$DEMOROOT$/en/angular/node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.import('@angular/compiler'); System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('$DEMOROOT$/en/lib/angular/license.ts'); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html>
<div class="sample-tutorial groupAppearanceSs"> <gc-spread-sheets [hostStyle]="hostStyle" (workbookInitialized)="initSpread($event)"> <gc-worksheet></gc-worksheet> <gc-worksheet></gc-worksheet> <gc-worksheet></gc-worksheet> <gc-worksheet></gc-worksheet> <gc-worksheet></gc-worksheet> </gc-spread-sheets> <div class="options-container"> <div id="settingsDiv"> <br/> <label>This text box shows sortState information about the last sort action.</label> <br/> <textarea id="showEventArgs" cols="85" rows="8" style="max-width: 98%"></textarea> <div class="option-row"> <input type="button" id="get_SortState_Btn" value="Get Sort State"/> </div> </div> </div> </div>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } input { display: inline-block; } input[type="text"] { width: 200px; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .groupAppearanceSs{ width: 100%; height: 100%; } .options-container { float: right; width: 280px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .option-row { font-size: 14px; padding: 5px; margin-top: 10px; } label { display: block; margin-bottom: 6px; } input { padding: 4px 6px; } input[type=button] { margin-top: 6px; display: block; }
(function (global) { System.config({ transpiler: 'ts', typescriptOptions: { tsconfig: true }, meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'core-js': 'npm:core-js/client/shim.min.js', 'zone': 'npm:zone.js/fesm2015/zone.min.js', 'rxjs': 'npm:rxjs/dist/bundles/rxjs.umd.min.js', '@angular/core': 'npm:@angular/core/fesm2022', '@angular/common': 'npm:@angular/common/fesm2022/common.mjs', '@angular/compiler': 'npm:@angular/compiler/fesm2022/compiler.mjs', '@angular/platform-browser': 'npm:@angular/platform-browser/fesm2022/platform-browser.mjs', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/fesm2022/platform-browser-dynamic.mjs', '@angular/common/http': 'npm:@angular/common/fesm2022/http.mjs', '@angular/router': 'npm:@angular/router/fesm2022/router.mjs', '@angular/forms': 'npm:@angular/forms/fesm2022/forms.mjs', 'jszip': 'npm:jszip/dist/jszip.min.js', 'typescript': 'npm:typescript/lib/typescript.js', 'ts': './plugin.js', 'tslib':'npm:tslib/tslib.js', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js', '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-angular': 'npm:@mescius/spread-sheets-angular/fesm2020/mescius-spread-sheets-angular.mjs', '@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'ts' }, rxjs: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, "node_modules/@angular": { defaultExtension: 'mjs' }, "@mescius/spread-sheets-angular": { defaultExtension: 'mjs' }, '@angular/core': { defaultExtension: 'mjs', main: 'core.mjs' } } }); })(this);