如何通过word-js替换大量内容控件中的文本?

时间:2017-07-25 19:12:58

标签: javascript ms-word ms-office office-js office-addins

我正在尝试编写一个函数,该函数将富文本内容控件列表和单个字符串作为参数,并使用此字符串替换所有匹配内容控件的内容。

虽然这适用于较少量的内容控件,但它会因包含大量内容控件的文档而失败。我必须处理包含700多个内容控件和单个标题的文档。在这种情况下,代码只替换前66X CC,然后使用GeneralException中止。我认为这只是由于大量的内容控件。当我尝试为所有这些CC(GeneralException)注册绑定时,我遇到了类似的问题。但这是一个不同的主题。

我试图解决这个问题,通过限制每个.sync()的更改量并循环通过CC,根据需要执行尽可能多的循环。然而,由于office-js的异步性质,这并不容易。到目前为止,我对javascript-async-promise-programming不是很熟悉。但这就是我想出来的:

function replaceCCtextWithSingleString (CCtitleList, string) {
    var maxPerBatch = 100;

    /*
     * A first .then() block is executed to get proxy objects for all selected CCs
     *
     * Then we would replace all the text-contents in one single .then() block. BUT:
     * Word throws a GeneralException if you try to replace the text in more then 6XX CCs in one .then() block.
     * In consequence we only process maxPerBatch CCs per .then() block
     */
    Word.run(function (context) {
        var CCcList = [];

        // load CCs
        for(var i = 0; i < CCtitleList.length; i++) {
            CCcList.push(context.document.contentControls.getByTitle(CCtitleList[i]).load('id'));
        }

        return context.sync().then(function () { // synchronous
            var CClist = [];
            // aggregate list of CCs
            for(var i = 0; i < CCcList.length; i++) {
                if(CCcList[i].items.length == 0) {
                    throw 'Could not find CC with title "'+CCtitleList[j]+'"';
                }
                else {
                    CClist = CClist.concat(CCcList[i].items);
                }
            }
            $('#status').html('Found '+CClist.length+' CCs matching the criteria. Started replacing...');
            console.log('Found '+CClist.length+' CCs matching the criteria. Started replacing...');

            // start replacing
            return context.sync().then((function loop (replaceCounter, CClist) {
                // asynchronous recoursive loop
                for(var i = 0; replaceCounter < CClist.length && i < maxPerBatch; i++) { // loop in loop (i does only appear in condition)
                    // do this maxPerBatch times and then .sync() as long as there are still unreplaced CCs
                    CClist[replaceCounter].insertText(string, 'Replace');
                    replaceCounter++;
                }

                if(replaceCounter < CClist.length) return context.sync()  // continue loop
                    .then(function () {
                        $('#status').html('...replaced the content of '+replaceCounter+' CCs...');
                        return loop(replaceCounter, numCCs);
                    });
                else  return context.sync() // end loop
                    .then(function () {
                        $('#status').html('Replaced the content of all CCs');
                    });
            })(0, CClist));
        });
    }).catch(function (error) {
        $('#status').html('<pre>Error: ' + JSON.stringify(error, null, 4) + '</pre>');
        console.log('Error: ' + JSON.stringify(error, null, 4));
        if (error instanceof OfficeExtension.Error) {
            console.log('Debug info: ' + JSON.stringify(error.debugInfo, null, 4));
        }
        throw error;
    });
}

然而......它不起作用。它取代了前100个CC,然后停止。没有失败,没有例外或任何事情。 return loop(replaceCounter, CClist);刚刚执行,我不知道为什么。如果我尝试在调试器中进入这一行,它会把我扔到office-js代码中。

有什么建议吗?

修改

我根据Juan Balmori的建议更新了我的代码,它起到了魅力作用:

    function replaceCCtextWithSingleString_v1_1 (CCtitleList, string) {
    Word.run(function (context) {
        var time1 = Date.now();

        // load the title of all content controls
        var CCc = context.document.contentControls.load('title');

        return context.sync().then(function () { // synchronous
            // extract CC titles
            var documentCCtitleList = [];
            for(var i = 0; i < CCc.items.length; i++) { documentCCtitleList.push(CCc.items[i].title); }

            // check for missing titles and replace
            for(var i = 0; i < CCtitleList.length; i++) {
                var index = documentCCtitleList.indexOf(CCtitleList[i]);
                if(index == -1) { // title is missing
                    throw 'Could not find CC with title "'+CCtitleList[i]+'"';
                }
                else { // replace
                    CCc.items[index].insertText(string, 'Replace');
                }
            }

            $('#status').html('...replacing...');

            return context.sync().then(function () {
                var time2 = Date.now();
                var tdiff = time2-time1;
                $('#status').html('Successfully replaced all selected CCs in '+tdiff+' ms');
            });
        });
    }).catch(function (error) {
        $('#status').html('<pre>Error: ' + JSON.stringify(error, null, 4) + '</pre>');
        console.log('Error: ' + JSON.stringify(error, null, 4));
        if (error instanceof OfficeExtension.Error) {
            console.log('Debug info: ' + JSON.stringify(error.debugInfo, null, 4));
        }
    });
}

完成仍需要13995毫秒,但至少它可行: - )

任何想法,是什么引起了GeneralException?

我发布了一个有关速度问题的新问题:What is the fastest way of replacing the text of many content controls via office-js?

1 个答案:

答案 0 :(得分:1)

好问题..很久以前我做了一些性能测试,我能够在文档中更改超过10k的内容控件。 700你应该没问题。 不确定为什么你预先填写一个列表,这是不需要的,你实际上导航2次收集,这对perf不利。您可以在遍历集合时进行字符串比较!

以下是一个示例,我刚刚对700内容控制文档进行了快速测试,该文档带有假设标记&#34; test&#34;。

我能够  1.将他们的文本与你想要比较的文本进行比较(它是一个字符串)  2.如果条件为真,则更改值。

完成操作需要5134毫秒,这是代码。我认为这是完全可以接受的。

希望这有帮助!

&#13;
&#13;
 function perfContentControls() {
        var time1 = Date.now(); // lets see in how much time we complete the operation :)
        var CCs =0
        Word.run(function (context) {
            var myCCs = context.document.body.contentControls.getByTag("test");
            context.load(myCCs);
            return context.sync()
            .then(function () {
                CCs  = myCCs.items.length
                for (var i = 0; i < CCs; i++) {
                    if (myCCs.items[i].text == "new text 3") // you can check the cc content and if needed replace it....
                         myCCs.items[i].insertText("new text 4", "replace");
                
                }

                return context.sync()
                .then(function () {
                    var time2 = Date.now();
                  var diff = time2 - time1;
                  console.log("# of CCs:" + CCs + " time to change:" + diff + "ms");
                })
            })
           .catch(function (er) {
               console.log(er.message);

           })

        })

    }
&#13;
&#13;
&#13;