Excel JS API:使用自定义函数从单元格写入,计算和加载值

时间:2019-01-17 18:48:54

标签: office365 office-js excel-addins custom-functions-excel

目的是将公式(自定义函数)写入单元格,对其进行计算,加载值并在单个函数中检索它们。

function myFunc() {
    Excel.run(function (ctx) {
        var fExcel = '=SUM(1,2)';
        var fCustom = '=custFunc()';

        var rng = ctx.workbook.worksheets.getActiveWorksheet().getRange('A1');

        //rng.formulas = [[fExcel]]; // works OK          
        rng.formulas = [[fCustom]]; // values are #GETTING_DATA

        // try different calc calls

        rng.load("values");

        return ctx.sync().then(function () {
            console.log(rng.values);
        });
    });
}

对于内置Excel函数,一切正常,并且consolectx.sync()之后记录一个值3。使用自定义函数(向外部服务器发送请求以计算结果)的值为'#GETTING_DATA'。在rng.load("values");之前,我已经尝试了以下所有操作来触发计算,但到目前为止没有任何效果:

  • rng.calculate();

  • var s = ctx.workbook.worksheets.getActiveWorksheet(); s.calculate(true);

  • ctx.workbook.application.calculate('Full');

是否可以触发自定义函数的计算并确保值在ctx.sync()之后可用?

1 个答案:

答案 0 :(得分:1)

有趣的场景!

今天,利用onCalculate事件,此可能是可行的,但需要注意的是,当您自定义函数正在计算时,它将触发2倍。

  • 这是因为自定义函数在后台计算时首先显示#GETTING_DATA。
    • 这使用户可以在功能仍在评估时进行后退控制,从而使应用程序的响应速度更快。此行为不同于可能会挂起Excel的VBA或XLL UDF。
  • 当Excel完成计算后,它将再次触发计算事件。这是通过兑现承诺而得到的结果。

Script lab gist应该向您说明其工作方式:

/*This gist works in combination with any registered Excel JS Custom function*/

$("#set-formulas").click(() => tryCatch(setFormulas));

var rangeToCheck;

async function setFormulas() {
  await Excel.run(async (context) => {
    //register for event
    context.workbook.worksheets.getActiveWorksheet().onCalculated.add(handleCalculate);

    //write to grid
    const sheet = context.workbook.worksheets.getItem("Sheet1");

    rangeToCheck = "A1";
    const range = sheet.getRange(rangeToCheck);
    range.formulas = [['=CONTOSO.CONTAINS(A1, Days)']];
    range.format.autofitColumns();

    await context.sync();
  });
}

async function handleCalculate(event) {
  //read cell
  console.log("calc ended - begin");

  console.log("Change type of event: " + event.changeType);
  console.log("Address of event: " + event.address);
  console.log("Source of event: " + event.source);

  //Read A1 and log it back to the console
  await Excel.run(async (context) => {
    //write to grid
    const sheet = context.workbook.worksheets.getItem("Sheet1");

    const range = sheet.getRange(rangeToCheck);
    range.load("values");

    await context.sync();

    if (range.values.toString() != "GETTING_DATA") {
      console.log("Success: " + range.values);
    }
  });
}

/** Default helper for invoking an action and handling errors. */
async function tryCatch(callback) {
  try {
    await callback();
  } catch (error) {
    OfficeHelpers.UI.notify(error);
    OfficeHelpers.Utilities.log(error);
  }
}