Summary
PivotTables can be sorted in the following ways:
Sort By Field Item Name
Sort By Value
Sort By Custom Field Item Value
Sort By Custom Callback
The sortType can be set in any way.
API
Interface
API
Sample Code
import { Component, NgModule, enableProdMode } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { SpreadSheetsModule } from '@mescius/spread-sheets-angular';
import { FormsModule } from '@angular/forms';
import GC from '@mescius/spread-sheets';
import "@mescius/spread-sheets-shapes";
import "@mescius/spread-sheets-pivot-addon";
import './styles.css';
function _getElementById(id) {
return document.getElementById(id);
}
@Component({
selector: 'app-component',
templateUrl: 'src/app.component.html'
})
export class AppComponent {
dataSource: any[];
spread: GC.Spread.Sheets.Workbook;
hostStyle = {
width: 'calc(100% - 400px)',
height: '100%',
overflow: 'hidden',
float: 'left'
};
pivotTable: any;
selectedSortField = "Salesperson";
sortFields = ["Salesperson", "Cars", "Date"];
selectedSortType = "0";
selectedValueFieldName = "";
valueFieldNames = ["", "Quantity"];
_rangeSelector: any;
constructor() {}
initSpread($Event: any) {
let spread = $Event.spread;
this.spread = spread;
spread.suspendPaint();
spread.setSheetCount(2);
let sheet1 = spread.getSheet(0);
let sheet2 = spread.getSheet(1);
let tableName = this.getSource(sheet2, pivotSales);
let pivotTable = this.addPivotTable(sheet1, tableName);
this.pivotTable = pivotTable;
this.initFormulaTextBox(spread);
spread.focus();
spread.resumePaint();
}
initFormulaTextBox(spread) {
let host = _getElementById("formulaTextBox");
this._rangeSelector = new GC.Spread.Sheets.FormulaTextBox.FormulaTextBox(host, {
rangeSelectMode: true,
absoluteReference: true,
needSheetName: false
}, spread);
this._rangeSelector.workbook(spread);
}
getSource(sheet: GC.Spread.Sheets.Workbook, tableSource: any) {
sheet.name("DataSource");
sheet.setRowCount(117);
sheet.setColumnWidth(0, 120);
sheet.getCell(-1, 0).formatter("YYYY-mm-DD");
sheet.getRange(-1,4,0,2).formatter("$ #,##0");
let table = sheet.tables.add('table', 0, 0, 117, 6);
for(let i=2;i<=117;i++)
{
sheet.setFormula(i-1,5,'=D'+i+'*E'+i)
}
table.style(GC.Spread.Sheets.Tables.TableThemes["none"]);
sheet.setArray(0, 0, tableSource);
return table.name();
}
setSortInfo (sortType: number) {
let spread = this.spread;
let pivotTable = this.pivotTable;
let _rangeSelector = this._rangeSelector;
spread.suspendPaint();
let sortInfo = this.generateSortInfo();
if (sortInfo) {
pivotTable.sort(this.selectedSortField, sortInfo);
this.syncSortInfo();
_rangeSelector.endSelectMode();
spread.focus();
}
spread.resumePaint();
}
clearSortInfo () {
let spread = this.spread;
let pivotTable = this.pivotTable;
let _rangeSelector = this._rangeSelector;
pivotTable.sort(this.selectedSortField, undefined);
this.syncSortInfo();
_rangeSelector.endSelectMode();
spread.focus();
}
generateSortInfo() {
let pivotReferences = this.initPivotReferences();
let sortInfo: any = {
sortType: +this.selectedSortType
};
if (this.selectedValueFieldName !== '') {
sortInfo.sortValueFieldName = this.selectedValueFieldName;
}
if (pivotReferences) {
sortInfo.sortByPivotReferences = pivotReferences;
}
return sortInfo;
}
syncSortInfo() {
let pivotTable = this.pivotTable;
let sortInfo = pivotTable.sort(this.selectedSortField);
let sortType = sortInfo && sortInfo.sortType;
if (sortType === undefined) {
sortType = GC.Spread.Pivot.SortType.asc;
}
this.selectedSortType = sortType + '';
let sortValueFieldName = sortInfo && sortInfo.sortValueFieldName;
if (sortValueFieldName === undefined) {
sortValueFieldName = '';
}
this.selectedValueFieldName = sortValueFieldName;
let pivotReferences = sortInfo && sortInfo.sortByPivotReferences;
this.setPivotReferences(pivotReferences);
}
initPivotReferences() {
let _rangeSelector = this._rangeSelector;
let pivotTable = this.pivotTable;
let cellRef = _rangeSelector.text();
if (!cellRef || !this.selectedValueFieldName) {
return;
}
let spread = this.spread;
let sheet = spread.getActiveSheet();
let range = GC.Spread.Sheets.CalcEngine.formulaToRange(sheet, _rangeSelector.text());
if (range) {
let row = range.row, col = range.col;
if (sheet.pivotTables.findPivotTable(row, col)) {
let pivotInfo = pivotTable.getPivotInfo(row, col);
let fieldArea = pivotTable.getField(this.selectedSortField).pivotArea;
let infos;
if (fieldArea === GC.Spread.Pivot.PivotTableFieldType.rowField) {
infos = pivotInfo.colInfos;
}
if (fieldArea === GC.Spread.Pivot.PivotTableFieldType.columnField) {
infos = pivotInfo.rowInfos;
}
if (infos && infos.length > 0) {
let isGrandTotal = infos.length === 1 && infos[0].isGrandTotal;
if (!isGrandTotal) { // if is grand total, nothing to do.
return infos.map((info) => {
return {
fieldName: info.fieldName,
items: [info.itemName]
};
});
}
}
}
}
}
setPivotReferences(pivotReferences) {
let pivotTable = this.pivotTable;
let rangeStr = '', resultRow, resultCol;
if (pivotReferences) {
let refSourceNames = pivotReferences.map(ref => ref.fieldName);
let allFields = pivotTable.getFieldsByArea(GC.Spread.Pivot.PivotTableFieldType.rowField).concat(pivotTable.getFieldsByArea(GC.Spread.Pivot.PivotTableFieldType.columnField));
let pivotArea = {
references: allFields.map(function (field) {
let index = refSourceNames.indexOf(field.sourceName);
if (index !== -1) {
return {
fieldName: field.fieldName,
items: pivotReferences[index].items
}
} else {
return {
fieldName: field.fieldName
}
}
})
}
let range = pivotTable.getPivotAreaRanges(pivotArea)[0];
resultRow = range.row;
resultCol = range.col;
rangeStr = GC.Spread.Sheets.CalcEngine.rangeToFormula(new GC.Spread.Sheets.Range(resultRow, resultCol, 1, 1));
}
this._rangeSelector.text(rangeStr);
}
addPivotTable(sheet: GC.Spread.Sheets.Workbook, source: any) {
sheet.suspendPaint();
sheet.name("PivotTable");
sheet.setRowCount(10000);
let pivotTable = sheet.pivotTables.add("PivotTable", source, 1, 1, GC.Spread.Pivot.PivotTableLayoutType.outline, GC.Spread.Pivot.PivotTableThemes.light8);
pivotTable.suspendLayout();
pivotTable.add("salesperson", "Salesperson", GC.Spread.Pivot.PivotTableFieldType.rowField);
pivotTable.add("car", "Cars", GC.Spread.Pivot.PivotTableFieldType.rowField);
let groupInfo = { originFieldName: "date", dateGroups: [{ by: GC.Pivot.DateGroupType.quarters }] };
pivotTable.group(groupInfo);
pivotTable.add("Quarters (date)", "Quarters (date)", GC.Spread.Pivot.PivotTableFieldType.columnField);
pivotTable.add("quantity", "Quantity", GC.Spread.Pivot.PivotTableFieldType.valueField, GC.Pivot.SubtotalType.sum);
pivotTable.options.subtotalsPosition = GC.Spread.Pivot.SubtotalsPosition.top;
pivotTable.resumeLayout();
sheet.resumePaint();
pivotTable.autoFitColumn();
return pivotTable;
}
}
@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/pivot-data.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">
<gc-spread-sheets [hostStyle]="hostStyle" (workbookInitialized)="initSpread($event)">
</gc-spread-sheets>
<div class="options-container">
<div class="options-row">
<span>Sort Field:</span>
<select id="sort-field" [(ngModel)]="selectedSortField" (change)="syncSortInfo($event)">
<option *ngFor="let field of sortFields">{{field}}</option>
</select>
</div>
<div class="options-row">
<span>Sort Type:</span>
<label><input type="radio" name="sort-type" value="0" [(ngModel)]="selectedSortType">ASC</label>
<label><input type="radio" name="sort-type" value="1" [(ngModel)]="selectedSortType">DESC</label>
</div>
<div class="options-row">
<span>Value Field Name:</span>
<select id="value-field-name" [(ngModel)]="selectedValueFieldName">
<option *ngFor="let field of valueFieldNames">{{field}}</option>
</select>
</div>
<div class="options-row">
<span>Cell Ref:</span>
<div id="formulaTextBox"></div>
</div>
<hr>
<div class="options-row">
<button class="set-sort-info-button" (click)="setSortInfo()">Set Sort Info</button>
</div>
<div class="options-row">
<button class="clear-sort-info-button" (click)="clearSortInfo()">Clear Sort Info</button>
</div>
</div>
</div>
.sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
.sample-spreadsheets {
width: calc(100% - 400px);
height: 100%;
overflow: hidden;
float: left;
}
.options-container {
float: right;
width: 380px;
padding: 10px;
font-family: Arial, sans-serif;
font-size: 14px;
}
.options-row {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.options-row span {
flex: 1;
margin-right: 10px;
min-width: 100px;
text-align: left;
}
.options-row select,
.options-row input[type="text"] {
flex: 2;
padding: 5px;
border: 1px solid #ccc;
border-radius: 3px;
font-family: inherit;
font-size: inherit;
}
.options-row label {
flex: 2;
}
.options-row label:first-of-type{
padding-left: 15px;
}
.options-row button {
flex: 1;
background-color: #007bff;
color: #fff;
padding: 5px 10px;
border: none;
border-radius: 3px;
font-family: inherit;
font-size: inherit;
cursor: pointer;
}
.options-row button:hover {
background-color: #0069d9;
}
#formulaTextBox {
flex: 2;
padding: 2px 5px;
border: 1px solid #ccc;
border-radius: 3px;
font-family: inherit;
font-size: inherit;
background-color: #fff;
overflow: hidden;
}
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
(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-shapes': 'npm:@mescius/spread-sheets-shapes/index.js',
'@mescius/spread-sheets-pivot-addon': 'npm:@mescius/spread-sheets-pivot-addon/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);