【问题标题】:Can the Excel context be passed safely to a class?Excel 上下文可以安全地传递给一个类吗?
【发布时间】:2019-07-25 21:10:01
【问题描述】:

我正在使用 Javascript 开发 Excel 插件。我遇到了一些非常奇怪的行为,所以我主要关心是否必须使用 context.trackedObjects.add() 。我的代码的一般流程是...

  1. 从用户那里获取一系列数据。 使用 Excel.run #1。
  2. 使用简单的 AJAX 调用调用 Azure 函数来计算一些统计信息。 使用 Excel.run #2
  3. 使用 TypeScript 类中的代码编写结果。 从第 2 步接收上下文

代码放在这里太长了,这是一个总结版本。请注意,在第一个 Excel.run 中引用上下文的所有对象都不会在第二个 Excel.run 中使用。传递给该类的唯一变量是 Excel 上下文和 JSON 字符串。

function UserClicksButton() {
   await Excel.run(async (ctx) => {  **//Excel.run #1**
       let MyData:string = await GetDataFromSheet(ctx); //step 1, get stringified JSON object

        GetStatsFromServer(MyData,ReturnObject); //step 2

   }
}

async function GetStatsFromServer(In_Data,Return_Object) {
   var settings = {
                "async": true,
                "crossDomain": true,
                //others removved
         },
    "data": In_Data
    }
    $.ajax(settings).done(function(response) {
         Excel.run(async (ctx) => {             **//Excel.run #2**
                    let myChart= new MyChart(); 

                    myChart.CreateChart(ctx, response);
                });
    }).
    .fail {//deal with fail}
}

class MyChart {
  m_ctx:Excel.RequestContext;
  m_AnswerString: String;

  public async CreateChart(ctx: Excel.RequestContext, AnswerString:string) {
     this.m_ctx=ctx;
     this.m_AnswerString = AnswerString;

     //create chart using this.m_ctx
     //Excel.run is not called in class at all
  }
}

我问的原因是我遇到了一些非常奇怪的错误。在某一时刻,我得到“对象路径 Chart.add 不适用于您要执行的操作。如果您在多个 \"context.sync\" 调用中使用该对象,并且在顺序执行之外".run\" 批处理,请使用 \"context.trackedObjects.add()\" 和 \"context.trackedObjects.remove()\" 方法来管理对象的生命周期。"

但是,根据我输入“await this.m_ctx.sync()”的位置,我会收到不同的错误或根本没有错误。例如,MyChart 类中的 CreateChart 函数相当长,因为它对图表进行了大量的格式化。它可以缩写为(为简洁起见省略变量声明)...

class MyChart {
  m_ctx:Excel.RequestContext;
  m_AnswerString: String;

  public async CreateChart(ctx: Excel.RequestContext, AnswerString:string) {
     this.m_ctx=ctx;
     this.m_AnswerString = AnswerString;

     //create chart using this.m_ctx
     newChart= sheet.charts.add("XYScatter", dataRange, "Auto");
     formatTitle(newChart);

     await this.formatAxis(newChart);
     //await this.m_ctx.sync(); //TODO: remove when done debugging
     await this.formatSeries0(newChart);
     //await this.m_ctx.sync(); //TODO: remove when done debugging
     await this.formatSeries1(newChart);
     //await this.m_ctx.sync(); //TODO: remove when done debugging
     await this.formatSeries2(newChart);
     //await this.m_ctx.sync(); //TODO: remove when done debugging
     await this.formatSeries3(newChart);
     //await this.m_ctx.sync(); //TODO: remove when done debugging
     await this.formatSeries4(newChart);

  }
}

注释掉的行“await this.m_ctx.sync();”我添加以查找有问题的代码行。但是,它会改变代码的行为。使用 await() 函数,我没有收到错误消息。随着等待,我有时会得到一个错误。

一些笔记。图表的创建都是“输出”。我没有从 Excel 上下文中加载任何内容。它是 100% 写入工作表的。我真的不需要函数是异步的,我只是让它们aysnc 能够调用await ctx.sync()。

在寻找问题的过程中,我发现这篇文章似乎并不适用,因为我没有在多个上下文之间共享变量。

Word Online Add-In: Using objects across multiple contexts

我知道我可以让函数 GetStatsFromServer 返回一个承诺,但根据 Zlatkovsky 的书,保持与 IE 的兼容性将其限制为 Excel 1.2 API,使用 Q 库(无法在我的 PC 上加载),或者获取进入我现在头顶的promise polyfill。虽然让这个函数返回一个 promise 会更干净,但现在我专注于这个问题,因为我的理解是它应该以这种方式工作。

【问题讨论】:

  • 重新。 “保持与 IE 的兼容性将其限制为 Excel 1.2 API”:事实并非如此。如果我的书中有什么地方让您产生了这种印象,请告诉我,以便我更正它!对于 Promise 的 polyfill,最简单的 polyfill 就是包含类似window.Promise = Office.Promise 这样的行,它可以满足您的需要。
  • 第 73 页,第 4.3.2 节列出了实现承诺的三种方法。我很确定我的帖子完全按照您的书的顺序列出了它们,即 OfficeExtension.Promise,它需要 ExcelAPI 1.2、Q 库和 promise polyfil。但是,除此之外,对我的问题的回答会更有帮助。 stackoverflow 的 Office-js 部分是一座鬼城。如果微软的人不回答这些问题,那么他们就不会得到回答。验证清单的系统似乎完全崩溃了。我已经发布了多个潜在的错误,但没有回复。
  • 我明白了。好的,就OfficeExtension.Promise 而言,这是有道理的,我将更新该部分。它经过更改和简化,因此现在如果您使用 window.Promise = Office.Promise(注意:Office 而不是 OfficeExtension),您甚至可以使用旧版本。

标签: office-js


【解决方案1】:

让我稍微笼统地回答这个问题,我认为您可以根据自己的情况进行调整。

首先:你应该总是(不,只是总是!)使用Excel.run。它会为你做很多正确的事情。我在我的书Building Office Add-ins using Office.js 的章节5.2: Excel.run (Word.run, etc.) 中描述了它到底做了什么。但我只想说,您最好在 Excel.run 块内执行所有对 Excel OM 的调用。

现在,对于您的场景:如果您的数据提取步骤(步骤 1)独立于您下次写入工作簿时(例如,您要将新图表放在全新的工作表上,那么您不需要引用与选择对应的原始Range 对象),我什至不用担心保留上下文!相反,只需执行您的第一个 Excel.run 以吸收所有数据,然后执行您的 AJAX 调用,然后执行一个新的 Excel.run 来处理结果。应该非常简单,并且具有使您的中间步骤完全隔离(因此可独立测试)的额外优势。

相反,如果您确实需要在步骤之间进行交互,那么您有两种选择:

  1. 执行一个大型的总体 Excel.run 获取数据,然后执行 AJAX 调用,然后写回工作簿 - 全部在一个 Excel.run 中。显然,您可以将其分解为辅助函数,但它仍然只是一个run。如果我们谈论的是 而不是 分钟,这是一个完全有效的选项。

  2. 为步骤#1 和步骤#3 中的每一个都分开Excel.run,但存储其中一个对象(例如,Range)以供以后在步骤#3 中使用。这在我的章节10.2: Using objects outside the "linear" Excel.run or Word.run flow (e.g., in a button-click callback, in a setInterval, etc.) 中有详细描述。这更困难并且需要更多的簿记,所以我只会在用户需要继续返回原始对象和/或如果它是用户启动的操作(例如,经过的时间可能是几分钟或小时)。

希望这会有所帮助!

~迈克尔

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-07
    • 1970-01-01
    • 2018-07-10
    • 1970-01-01
    • 2020-07-29
    • 2012-12-13
    相关资源
    最近更新 更多