简体   繁体   中英

Word Online Add-In: Using objects across multiple contexts

I'm attempting to make an add-in, whereby a paragraph is split into sentences and then spoken, with the sentences being highlighted as they are read. For the most part, I have this working but the issue comes when I want to highlight the sentences currently being read.

I have a function which splits the paragraph into sentences, based on where the users cursor is:

function selectionTest() {
    Word.run(function (context) {
        originalRange = context.document.getSelection();
        var paragraphs = originalRange.paragraphs;

        context.load(paragraphs, 'text');
        context.load(originalRange, 'text');

        return context.sync().then(function () {
            //Range should only be present in a single paragraph, rather than spanning multiple
            if (paragraphs.items.length === 1) {
                var paragraph = paragraphs.items[0];

                var ranges = paragraph.getTextRanges(['.'], true);

                context.load(ranges, 'text');
                return context.sync(ranges);
            }
        }).then(function (sentences) {
            ...

I want to base highlighting the text on the 'originalRange' object, so that the correct sentences are highlighted, like the following:

function highlightSentence(id, colour) {
    Word.run(function (context) {
        var paragraphs = originalRange.paragraphs;

        context.load(paragraphs, 'text');
        context.load(originalRange, 'text');
        ...

But this produces an error as 'originalRange' is being used over multiple contexts. Is there a way for me to use 'originalRange' over multiple contexts or another solution?

UPDATE:

I attempted to get the sentences of the paragraphs again within the function, using 'context.trackedObjects.add' for the original range. This caused the same error when attempting to get the 'paragraphs' property from it.

I realised that all I potentially needed was the sentences of the paragraphs, instead of using the original range to get the sentences again. Instead, I implemented a different solution:

function highlightSentence(id, colour) {
    Word.run(function (context) {
        context.trackedObjects.add(gSentences);

        return context.sync().then(function () {
            gSentences.items[id].font.highlightColor = colour;
        }).then(context.sync);

    }).then(function(){   
        gSentences.context.trackedObjects.remove(gSentences);
        gSentences.context.sync();
    }).catch(function(error) {
        console.log(error.message);
    });
}

However, I now get the following error: "The object path '_reference()' 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."

UPDATE:

I managed to solve the issue above. However, now, during the highlight function, since the 'gSentences' variable has not been loaded within the context, its properties such as 'font' are not available, so I am unable to change the highlight colour. If I attempt to load it in the context, the original error of 'cannot use objects across contexts' appears. I'm not sure what to do at this point.

UPDATE:

This is what I use to retrieve sentences in a paragraph that are either the same position or after the cursor. These sentences are pushed to an array to be spoken. I found that I had to mess around a lot with callbacks to do this.

function selectionTest() {
    Word.run(function (context) {
        var range = context.document.getSelection();
        var paragraphs = range.paragraphs;

        context.load(paragraphs, 'text');

        return context.sync().then(function () {
            if (paragraphs.items.length === 1) {
                var paragraph = paragraphs.items[0];

                gSentences = paragraph.getTextRanges(['.'], true);

                context.load(gSentences);
                return context.sync();
            }
        }).then(function () {
            if (gSentences.items) {
                var sentencesResult = '';
                var callbacklist = [];

                currentSentence = 0;
                sentencesToSpeak = [];

                function isSentenceinRange(idx, fn) {
                    var rangeLoc = gSentences.items[idx].compareLocationWith(range);

                    return context.sync().then(function () {
                        if (rangeLoc.value === Word.LocationRelation.contains || rangeLoc.value === Word.InsertLocation.after) {
                            return fn(gSentences.items[idx].text);
                        }

                        return fn('');
                    });
                }

                for (var i = 0; i < gSentences.items.length; i++) {
                    callbacklist.push(
                        (function (i) {
                            return function () {
                                isSentenceinRange(i, function (result) {
                                    if (result) {
                                        sentencesToSpeak.push({ id: i, text: result });

                                        if (i === gSentences.items.length - 1) {
                                            sentencesFinialised();
                                        }
                                    }
                                });
                            }
                        })(i)
                    );
                }

                for (var callback in callbacklist) {
                    callbacklist[callback].call(this);
                }

            }
        });
    }).catch(function (error) {
        console.log(error.message);
    });
}

I wanted to highlight the sentences while they were being spoken, which is what the next function would be used to do (called on the onend event listener of audio element)

 function highlightSentenceTest(id, colour) {
    Word.run(function (context) {
        context.trackedObjects.add(gSentences);
        //Causes error, but need to load to get access?
        context.load(gSentences);

        return context.sync().then(function () {
            gSentences.items[id].font.highlightColor = colour;

        }).then(context.sync)
    }).catch(function(error) {
        console.log(error.message);
    });
}

Very good question! It sounds a lot like something that I answered here: How can a range be used across different Word.run contexts?

If that doesn't help, please leave a comment with what is different about your scenario, and I can try to help.

~ Michael Zlatkovsky, developer on Office Extensibility team, MSFT

PS: Please tag your questions with office-js to make sure that we (the product group, and the community as well) see them.


Update based on the updated question/code:

Truthfully, it is somewhat unclear what your callbacks are doing... but let me give you some general guidance, and see if that helps:

  • For any object created during a Word.run(function(ctx) { ... }) , the object will become invalid as soon as Word.run is done executing. I don't mean it in a JS garbage-collection sense, but rather in the "will-no-longer-be-bound-to-the-document" sense. So even if you create a callback function within Word.run trying to capture scope, that won't work in of itself.

  • When you do want to preserve an object between executions (or rather, let it live beyond the Excel.run ), you must add it to ctx.trackedObjects . You need to do that within the .run , or else it'll be too late.

  • To use the object thereafter, you can no longer use Word.run , but instead use the context directly.

    savedObject.doSomething(); // or savedObject.load("someProperty"); savedObject.context.sync() .then(...) // optional .catch(...);

  • As an aside: we are actively working on making this pattern easier, so stay tuned...

  • For now, you need to use the TrackedObjects workaround (extra code) described on How can a range be used across different Word.run contexts? , though we should be updating Office.js fairly soon with the fix.

Does that help you get unblocked?

~ Michael

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM