I'm working on an Excel AddIn using Javascript. I'm getting some very strange behavior, so I'm mainly concerned if I have to use context.trackedObjects.add() or not. The general flow of my code is ...
- Get a range of data from the user. Uses Excel.run #1.
- Call an Azure function using a simple AJAX call to calculate some statistics. Uses Excel.run #2
- Write the results using code in a TypeScript class. Recieves context from step 2
The code is way to long to put here, this is a summarized version. Note that none of the objects the reference the context in the first Excel.run are used in the second Excel.run. The only variables being passed to the class are the Excel context and the JSON string.
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
}
}
The reason I'm asking is that I'm getting some very strange errors. At one point I got "The object path Chart.add isn't working for what you're trying to do. If you're using the object across multiple \"context.sync\" calls and outside the sequential execution of a \".run\" batch, please use the \"context.trackedObjects.add()\" and \"context.trackedObjects.remove()\" methods to manage the object's lifetime."
However, depending on where I put in "await this.m_ctx.sync()" I get different errors or no errors at all. For example, the CreateChart funciton in the MyChart class is quite long as it does a ton of formatting of the chart. It could be abbreviated as (variable declarations omitted for brevity) ...
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);
}
}
The commented out line "await this.m_ctx.sync();" I added to find the offending line of code. However, it changes the behavior of the code. With the await() functions, I don't get an error. With the awaits, I sometimes get an error.
A couple of notes. The creation of the chart is all "output". I'm not loading anything from the Excel context. It is 100% writing to the worksheet. I don't really need the functions to be asynchronous, I only made them aysnc to be able to call await ctx.sync().
In searching for the problem, I found this article which doesn't seem to apply since I'm not sharing variables between multiple contexts.
Word Online Add-In: Using objects across multiple contexts
I know that I could make the function GetStatsFromServer return a promise, but per Zlatkovsky's book, maintaining compatibility with IE limits this to the Excel 1.2 API, using the Q library (which is failing to load on my PC), or get into a promise polyfill which is over my head right now. While making this function return a promise would be cleaner, right now I'm focused on this problem as my understanding is that it should work this way.