【问题标题】:Document not in sync after replace text替换文本后文档不同步
【发布时间】:2018-03-03 12:44:17
【问题描述】:

我正在尝试替换 Word Online 文档中的某些文本,但无法正常工作。

'{{test}} , [[test]] , {test}' 结果为 '13 , 2 , 3' 而不是 '1 , 2 , 3'。

第一个文本似乎被处理了两次。

非常感谢任何帮助!

Office.initialize = function(reason) {

    function ready() {

        var myTags = [
            { "value": "1", "text": "{{test}}" },
            { "value": "2", "text": "[[test]]" },
            { "value": "3", "text": "{test}" }
        ];

        async function FillTag(tag) {

            await Word.run(async function(context) {

                    var options = Word.SearchOptions.newObject(context);
                    options.matchWildCards = false;

                    var searchResults = context.document.body.search(tag.text, options);
                    context.load(searchResults, 'text');

                    await context.sync();

                    searchResults.items.forEach(function(item) {
                        item.insertText(tag.value, Word.InsertLocation.replace);
                    });
                    await context.sync();
                })
                .catch(function(error) {
                    console.log('Error: ' + JSON.stringify(error));
                    if (error instanceof OfficeExtension.Error) {
                        console.log('Debug info: ' + JSON.stringify(error.debugInfo));
                    }
                });
        }

        async function ProcessArray(myTags) {
            myTags.forEach(async function(tag) {
                await FillTag(tag);
            });
        }

        ProcessArray(myTags);
    }

    if (document.readyState !== 'loading') {
        ready();
    }
    else {
        document.addEventListener('DOMContentLoaded', ready);
    }
};

【问题讨论】:

  • 请提供您正在搜索的文档内容的一个小示例。
  • 嗨 Rick,只需将这句话 {{test}} , [[test]] , {test} 粘贴到空白文档中。我希望我能正确理解你。在我的示例中,我正在搜索的标签存储在 myTags 对象中。
  • @DutchDan -- 我已经重现了您的问题,现在正在排除故障。希望很快会为您提供更多信息。
  • 嗨,Kim,非常感谢您的收看。我一直在尝试在正文中搜索标签,然后选择并删除它。如果你这样做了,那么文本就消失了,但仍然可以在背景中看到选择。我的感觉是留下了一些东西。
  • @DutchDan -- 好像我想通了...请在下面查看我的答案。

标签: office-js


【解决方案1】:

在您的ProcessArray() 函数中,尝试将forEach 语句替换为for...of 语句,如下所示:

async function ProcessArray(myTags) {
    for (var tag of myTags) {
        await FillTag(tag);
    }
}

似乎forEach 语句触发了多个异步调用,而实际上每次都没有等待FillTag 的完成。如果您将forEach 语句替换为for...of,如上所示,您应该会得到预期的结果。


UPDATE(附加信息代码结构):

@DutchDan -- 既然您最初的问题已经解决,这里有一种更优化的方式来构建您的代码。

Office.initialize = function () {
    $(document).ready(function () {        
        FindAndReplace();
    });
};

async function FindAndReplace() {

    var myTags = [
        { "value": "1", "text": "{{test}}" },
        { "value": "2", "text": "[[test]]" },
        { "value": "3", "text": "{test}" }
    ];

    await Word.run(async (context) => {

        for (var tag of myTags) {
            var options = Word.SearchOptions.newObject(context);
            options.matchWildCards = false;

            var searchResults = context.document.body.search(tag.text, options);

            context.load(searchResults, 'text');

            await context.sync();

            searchResults.items.forEach(function (item) {
                item.insertText(tag.value, Word.InsertLocation.replace);
            });

            await context.sync();
        }
    }).catch(errorHandler);
}

注意:您可以使用 Script Lab (https://aka.ms/getscriptlab) 自己快速轻松地尝试这个 sn-p。只需安装 Script Lab 插件(免费),然后在导航菜单中选择“导入”,并使用以下 Gist URL:https://gist.github.com/kbrandl/b0c9d9ce0dd1ef16d61372cb84636898

【讨论】:

  • 金,你是最棒的!!它按预期工作。从来没有想过会有我的错误。再次非常感谢!
  • @DutchDan,很高兴我能帮上忙。我还更新了我的答案以包含一个代码示例,该示例显示了构建代码的更优化方式。
  • 很好的答案。请参阅 2018 年 12 月 15 日对我的答案的编辑,以了解在搜索标签数量更多的情况下可能会更好的替代方案。
【解决方案2】:

这与其说是一个答案,不如说是一个调试建议,但可以稍后进行编辑。请将Script Lab tool from AppSource 安装到 Word 中。您可以在其中找到一个示例 sn-ps,称为 Search。 sn-p 中的功能之一是 basicSearch。我将搜索文本“在线”替换为“{{test}}”,并将以黄色突出显示找到的文本的行替换为以下行:

results.items[i].insertText("1", Word.InsertLocation.replace);

这很好用,所以在足够简单的场景中,它可以准确地找到并替换“{{test}}”。

您能否自己尝试一下,然后逐渐改变方法,使其更接近您的方法,看看它从什么时候开始失效?


编辑 1/15/18:

@Kim Brandl 的答案可能最适合您,假设您确实只有 3 个搜索字符串。但是,它确实有一个循环内的context.sync。由于每次同步都是到 Office 主机的往返,因此当输入数量很大和/或加载项在 Office Online 中运行时(这意味着 Office 主机在同一机器)。

对于阅读本文且输入字符串较多的任何人,这里有一个解决方案,可保证整个Word.run 中需要的同步不超过 3 个。它还直接攻击您尝试解决的问题的根源,即某些已找到范围与其他范围的相对位置(具体而言,有些在其他范围内)。

我在Word-Add-in-Angular2-StyleChecker中也使用的策略是首先加载所有范围,然后使用Range.compareLocationWith方法和LocationRelation枚举来查找您需要的相对位置信息。最后,使用每个范围与其他范围的相对位置来确定是否/如何处理它。

这里是函数。按照 Kim 的示例,我将整个 sn-p 放入此 gist,您可以将其导入到 Script Lab tool from AppSource。 (请参阅 Kim Brandl 的回答中的说明。)

async function FindAndReplace() {

    let myTags = [
        { "value": "1", "text": "{{test}}" },
        { "value": "2", "text": "[[test]]" },
        { "value": "3", "text": "{test}" },
        { "value": "4", "text": "bob" },
        { "value": "5", "text": "bobb" },
        { "value": "6", "text": "ssally" },
        { "value": "7", "text": "sally" }
    ];

    let allSearchResults = [];

    await Word.run(async (context) => {    
        for (let tag of myTags) {    
            let options = Word.SearchOptions.newObject(context);
            options.matchWildCards = false;
            let searchResults = context.document.body.search(tag.text, options);
            searchResults.load('text');

            // Store each set of found ranges and the text that should replace 
            // them together, so we don't have to reconstruct the correlation 
            // after the context.sync.
            let correlatedSearchResult = {
                searchHits: searchResults, 
                replacementString: tag.value
            }           
            allSearchResults.push(correlatedSearchResult);       
        }

        await context.sync();

        // Now that we've loaded the found ranges we correlate each to
        // its replacement string, and then find each range's location relation
        // to every other. For example, 'bob' would be Inside 'xbobx'. 
        let correlatedFoundRanges = [];
        allSearchResults.forEach(function (correlatedSearchResult) {
            correlatedSearchResult.searchHits.items.forEach(function (foundRange) {
                let correlatedFoundRange = {
                    range: foundRange,
                    replacementText: correlatedSearchResult.replacementString,
                    locationRelations: []
                }
                correlatedFoundRanges.push(correlatedFoundRange);                
            });
        });

        // Two-dimensional loop over the found ranges to find each one's 
        // location relation with every other range.
        for (let i = 0; i < correlatedFoundRanges.length; i++) {
            for (let j = 0; j < correlatedFoundRanges.length; j++) {
                if (i !== j) // Don't need the range's location relation with itself.
                {
                    let locationRelation = correlatedFoundRanges[i].range.compareLocationWith(correlatedFoundRanges[j].range);
                    correlatedFoundRanges[i].locationRelations.push(locationRelation);
                }
            }
        }

        // It is not necesary to *explicitly* call load() for the 
        // LocationRelation objects, but a sync is required to load them.
        await context.sync();    

        let nonReplaceableRanges = [];
        correlatedFoundRanges.forEach(function (correlatedFoundRange) {
            correlatedFoundRange.locationRelations.forEach(function (locationRelation) {
                switch (locationRelation.value) {
                    case "Inside":
                    case "InsideStart":
                    case "InsideEnd":

                        // If the range is contained inside another range,
                        // blacklist it.
                        nonReplaceableRanges.push(correlatedFoundRange);
                        break;
                    default:
                        // Leave it off the blacklist, so it will get its 
                        // replacement string.
                        break;
                }
            });
        });

        // Do the replacement, but skip the blacklisted ranges.
        correlatedFoundRanges.forEach(function (correlatedFoundRange) {
            if (nonReplaceableRanges.indexOf(correlatedFoundRange) === -1) {
                correlatedFoundRange.range.insertText(correlatedFoundRange.replacementText, Word.InsertLocation.replace);
            }
        })

        await context.sync();
    });
}

【讨论】:

  • 嗨瑞克,很抱歉这么含糊!问题不在于文本没有被替换,而是“{{test}}”被替换了两次。首先是它的值 1(参见 myTag 对象),后来也添加了数字 3。因此,通过迭代,在 {{test}} 中发现了三个 {test},它应该被替换为 1。我让它听起来很复杂,但 {{test}} 被替换为 1,但仍然有一些幽灵文本,所以 {test} 也是成立。所以替换应该看起来像 1 , 2 , 3 但它最终是 13 , 2 , 3
  • 其实你的问题很清楚。我只是匆匆回答。为了弥补这一点,我使用替代解决方案对其进行了编辑,在某些情况下可能比@Kim Brandl 的解决方案更好。
  • 嗨 Rick,很抱歉这么晚才做出反应,但只是想感谢您提供上面那段令人惊叹的代码!它极大地帮助了我更好地理解幕后发生的事情,它充当了我的插件新版本的蓝图。再次感谢。
猜你喜欢
  • 2019-12-12
  • 2020-11-19
  • 1970-01-01
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 2014-07-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多