Incremental Calculation

SpreadJS supports the Incremental Calculation mode, which the calculation won't block the UI for a long time.

Description
app.vue
index.html
Copy to CodeMine

With incremental calculation, SpreadJS will calculate the cells with the period, and check and respond to the UI events every period. This prevents the UI from freezing for a long time when the workbook contains many formulas.

In order to use the incremental calculation feature, you need to set the workbook option incrementalCalculation to true or load the file with incrementalCalculation. For example:

spread.options.incrementalCalculation = true;
spread.import(file, function() {}, function() {}, { incrementalCalculation: true });

When incremental calculation, user can get the calculation progress by the CalculationProgress event.

For example:

spread.options.incrementalCalculation = true;
spread.bind(GC.Spread.Sheets.Events.CalculationProgress, function (e, info) {
     var msg = "Calculate ";
     if (info.pendingCells === 0) {
         msg += "finished";
     } else if (info.iterate >= 0) {
         msg += info.pendingCells + " cells in iterative calculation round " + info.iterate;
     } else {
         msg += (info.totalCells - info.pendingCells) + "/" + info.totalCells + "cells";
     }   
     console.log(msg)
});
With incremental calculation, SpreadJS will calculate the cells with the period, and check and respond to the UI events every period. This prevents the UI from freezing for a long time when the workbook contains many formulas. In order to use the incremental calculation feature, you need to set the workbook option incrementalCalculation to true or load the file with incrementalCalculation. For example: When incremental calculation, user can get the calculation progress by the CalculationProgress event. For example:
<template> <div class="sample-tutorial"> <div class="sample-container"> <gc-spread-sheets class="sample-spreadsheets" @workbookInitialized="initSpread"> <gc-worksheet> </gc-worksheet> </gc-spread-sheets> <div id="statusBar"></div> </div> <div class="options-container"> <div class="option-row"> <div class="inputContainer"> <input id="selectedFile" type="file" accept=".sjs, .xlsx, .xlsm, .ssjson, .json, .csv" v-on:change="selectedFileChange($event)" /> <button class="settingButton" id="open" v-on:click="open">Open</button> <div class="options"> <div class="item"> <input type="checkbox" id="enable-incremental-calculation" v-on:change="optionChanged" checked /> <label for="enable-incremental-calculation">Enable incremental calculation</label> </div> </div> <button class="settingButton" id="open" v-on:click="calculateAll">Calculate All</button> </div> </div> </div> </div> </template> <script setup> import GC from "@mescius/spread-sheets"; import '@mescius/spread-sheets-print'; import '@mescius/spread-sheets-io'; import '@mescius/spread-sheets-shapes'; import '@mescius/spread-sheets-charts'; import '@mescius/spread-sheets-slicers'; import '@mescius/spread-sheets-pivot-addon'; import '@mescius/spread-sheets-reportsheet-addon'; import "@mescius/spread-sheets-tablesheet"; import "@mescius/spread-sheets-ganttsheet"; import { ref } from 'vue'; let mySpread; let selectedFileRef; function initSpread (spread) { mySpread = spread; const statusBar = new GC.Spread.Sheets.StatusBar.StatusBar( document.getElementById('statusBar') ); statusBar.bind(spread); fetch(getFileUrl()).then((res) => res.blob()).then((file) => spread.open(file, null, null, { openMode: 2, // incremental loading fullRecalc: true, incrementalCalculation: true }) ); } function selectedFileChange (e) { selectedFileRef = e.target.files[0]; } function optionChanged (e) { mySpread.options.incrementalCalculation = e.target.checked; } function calculateAll () { mySpread.calculate(); } function getFileUrl () { return '$DEMOROOT$/spread/source/data/incremental-calculation.sjs'; } function isSJSFile (file) { const fileName = file.name; const extensionName = fileName.substring(fileName.lastIndexOf('.') + 1); return extensionName === 'sjs'; } function open () { const file = selectedFileRef; if (!file) { return; } const options = { openMode: 2, // incremental loading fullRecalc: true, dynamicReferences: false, incrementalCalculation: mySpread.options.incrementalCalculation }; if (isSJSFile(file)) { mySpread.open(file, function () {}, function () {}, options); } else { mySpread.import(file, function () {}, function () {}, options); } } </script> <style scoped> #app { height: 100%; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-container { width: calc(100% - 280px); height: 100%; float: left; } .sample-spreadsheets { width: 100%; height: calc(100% - 25px); overflow: hidden; } #statusBar { bottom: 0; height: 25px; width: 100%; position: relative; } .options-container { float: right; width: 280px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .sample-options { z-index: 1000; } .inputContainer { width: 100%; height: auto; border: 1px solid #eee; padding: 6px 12px; margin-bottom: 10px; box-sizing: border-box; } .settingButton { color: #fff; background: #82bc00; outline: 0; line-height: 1.5715; position: relative; display: inline-block; font-weight: 400; white-space: nowrap; text-align: center; height: 32px; padding: 4px 15px; font-size: 14px; border-radius: 2px; user-select: none; cursor: pointer; border: 1px solid #82bc00; box-sizing: border-box; margin-bottom: 10px; margin-top: 10px; } .settingButton:hover { color: #fff; border-color: #88b031; background: #88b031; } .options-title { font-weight: bold; margin: 4px 2px; } #selectedFile { width: 180px; } #saveFileType { width: 120px; height: 31px; } .options .item { margin: 5px 0px; display: flex; } .save-options .item { margin: 5px 0px; display: flex; } label { margin-left: 3px; } select, input[type="text"], input[type="number"] { display: inline-block; margin-left: auto; width: 120px; font-weight: 400; outline: 0; line-height: 1.5715; border-radius: 2px; border: 1px solid #F4F8EB; box-sizing: border-box; } </style>
<!DOCTYPE html> <html lang="en" style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>SpreadJS VUE</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/vue3/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/en/vue3/node_modules/systemjs/dist/system.src.js"></script> <script src="./systemjs.config.js"></script> <script src="./compiler.js" type="module"></script> <script> var System = SystemJS; System.import("./src/app.js"); System.import('$DEMOROOT$/en/lib/vue3/license.js'); </script> </head> <body> <div id="app"></div> </body> </html>
(function (global) { SystemJS.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, packageConfigPaths: [ './node_modules/*/package.json', "./node_modules/@mescius/*/package.json", "./node_modules/@babel/*/package.json", "./node_modules/@vue/*/package.json" ], map: { '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-print': 'npm:@mescius/spread-sheets-print/index.js', '@mescius/spread-sheets-vue': 'npm:@mescius/spread-sheets-vue/index.js', '@mescius/spread-sheets-io': 'npm:@mescius/spread-sheets-io/index.js', '@mescius/spread-sheets-charts': 'npm:@mescius/spread-sheets-charts/index.js', '@mescius/spread-sheets-shapes': 'npm:@mescius/spread-sheets-shapes/index.js', '@mescius/spread-sheets-slicers': 'npm:@mescius/spread-sheets-slicers/index.js', '@mescius/spread-sheets-pivot-addon': 'npm:@mescius/spread-sheets-pivot-addon/index.js', '@mescius/spread-sheets-reportsheet-addon': 'npm:@mescius/spread-sheets-reportsheet-addon/index.js', '@mescius/spread-sheets-tablesheet': 'npm:@mescius/spread-sheets-tablesheet/index.js', '@mescius/spread-sheets-ganttsheet': 'npm:@mescius/spread-sheets-ganttsheet/index.js', 'vue': "npm:vue/dist/vue.esm-browser.js", 'tiny-emitter': 'npm:tiny-emitter/index.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', "systemjs-babel-build": "npm:systemjs-plugin-babel/systemjs-babel-browser.js", }, meta: { '*.css': { loader: 'systemjs-plugin-css' }, '*.vue': { loader: "../plugin-vue/index.js" } } }); })(this);