为什么这个 Context.Sync 不起作用?

Why does this Context.Sync not work?

为什么除非我取消对 range.values=range.values 行的注释,否则此代码片段不会将值写回 Excel?

$('#run').click(function() {
    invokeRun()
        .catch(OfficeHelpers.logError);
});
function invokeRun() {
    return Excel.run(function(context) {
        var range = context.workbook.worksheets.getItem("Sheet1").getRange("A1:B3");
        range.load('values');
        return context.sync()
            .then(function() {
               range.values[1][1]=99;
               console.log(JSON.stringify(range.values));
                //range.values=range.values
                return context.sync();
        });
    });
}

数组属性很特殊。我在我的网站上添加了一个页面来描述这个主题:Reading and writing array properties.

从那里总结一下,代理对象模型的工作方式,无论何时在对象上设置 属性,Office.js 运行时都会挂钩到 setter 并且getter,用于拦截调用,将命令加入队列

我们先举一个常规的例子属性。根据上述内容,每当您设置类似 range.format.fill.color = "red" 的内容时,颜色 属性 的 setter 会拦截请求并在内部向队列中添加一个命令以将范围填充颜色设置为红色(以与下一个一起发送 context.sync)

另一方面,如果你只有 var color = range.format.fill.color (当然,在 loadsync 之后),getter 将代替 setter 触发,颜色变量将获得范围的当前填充颜色。

现在,那是常规属性。每当您设置数组的 元素时 ,您实际上是在以 getter 的形式访问数组值。从运行时的角度来看,这一行与稍微冗长的版本没有什么不同:

var array = range.values;
array[r][c] = '-';

因为 range.values return 的 getter 是一个完全普通的 JS 数组对象,访问它然后设置它的值不会将它传播回原始 Range 对象。

如果您希望值得到反映,最好的办法是在同步后立即获取对数组的引用(即,var array = range.values,就像上面一样),然后设置根据需要在数组上设置值,然后最后将其设置回对象:range.values = array.

这意味着您还可以就地修改值数组,然后在循环完成时将值 属性 分配回自身 (range.values = range.values)。然而,这看起来很尴尬,好像它是一个空操作,而实际上它不是。所以就个人而言,我更喜欢在开始时检索数组并将其分配给自己的变量,然后进行任何必要的修改,最后将完整的数组设置回去。

更新以澄清以上内容:

非常清楚,数组 return 通过访问 .values.formulas 纯香草 JS阵列。这其实就是问题的症结所在:为了Office.js到return纯对象,意味着那些纯对象不能"spiked"具有反映变化的能力。

值得一提的是,我们实际上有一个即将推出的功能,应该会在一两个月内推出,我们将在其中引入 object.set 语法,如:

range.set({
   values: [[1, 2], [3, 4]],
   format: {
       fill: {
           color: "purple"
       }
   }
}

这将使在同一对象上设置多个属性更加方便,但也可能使数组属性更易于处理。