ms-wordoffice-jsoffice-addinsword-addinsword-web-addins

Word JS API: Range.compareLocationWith not working


I'm trying to determine if the user has clicked in another paragraph. My approach: The former selected paragraph gets stored locally as a Range object to be compared with later, when another click happens.

This is the code to get the selected paragraph:

export const getSelectedParagraphRange = async () => {
  return await Word.run(async (context) => {
    const range = context.document
      .getSelection()
      .paragraphs.getFirst()
      .getRange()

    return range
  })
}

This is the code to compare the ranges:

export const isParagraphStillActive = async (range: Word.Range) => {
  return await Word.run(async (context) => {
    const newRange = context.document
      .getSelection()
      .paragraphs.getFirst()
      .getRange()

    const compare = range.compareLocationWith(newRange)
    await context.sync()

    return compare.value === 'Equal'
  })
}

If I compare the newRange with itself it works (I get a compare object with value "Equal"). But if I compare the parameter range with itself it doesn't work (the function just "stops" without throwing an error etc).

So my guess is that the range objects which should be compared have to be taken directly from inside the function? If so, then I'm wondering how I can compare a former selected paragraph with a new one? Any help is greatly appreciated.

UPDATE:

Thanks to studying the mentioned Building Office Add-ins book I managed to get the paragraph comparison work. The getSelectedParagraphRange function lacked the tracking of the range object by calling range.track(). Additionally the tracked ranges have to be untracked when setting a new paragraph as the current one. (I simply wasn't aware of the principles behind the isolated context scopes).

Here's the working code:

export const getSelectedParagraphRange = async (formerRange?: Word.Range) => {
  return await Word.run(async (context) => {
    if (formerRange) {
      formerRange.untrack()
      await context.sync()
    }

    const range = context.document
      .getSelection()
      .paragraphs.getFirst()
      .getRange()
    range.track()
    await context.sync()

    return range
  })
}

export const isParagraphStillActive = async (range: Word.Range) => {
  return await Word.run(range, async (context) => {
    const newRange = context.document
      .getSelection()
      .paragraphs.getFirst()
      .getRange()

    const compare = range.compareLocationWith(newRange)
    await context.sync()

    return compare.value === 'Equal'
  })
}

Solution

  • You need to pass the range object to the overload of Word.run that takes an object as the first parameter.

    export const isParagraphStillActive = async (range: Word.Range) => {
      return await Word.run(range, async (context) => {
        const newRange = context.document
         .getSelection()
         .paragraphs.getFirst()
         .getRange()
    
        const compare = range.compareLocationWith(newRange)
        await context.sync()
    
        return compare.value === 'Equal'
      })
    }
    

    See Word.run. I also recommend the book Building Office Add-ins for advanced scenarios like this one.