似乎无法在 Office JS 中跨 Word.run(context => {...}) 调用使用范围

mxc*_*mxc 6 typescript office-js

我的项目涉及解析 Word 文档以提取一组标记,然后允许用户有选择地对这些标记执行批量操作。由于 UI 步骤,我需要能够调用myRange.select()和从提取令牌的 runmyRange.font.set(...)的后续Word.run(ctx => {...})调用。

如果这更适合作为 stackoverflow 帖子,我深表歉意并会在那里重新发布,但据我所知,该库似乎与 API 不匹配。不过我肯定会弄错。

预期行为

我希望调用Word.run(ctx => myRange.select())会导致选择该范围,因为该范围是在context.trackedObjects上一次运行时添加到的。

当前行为

什么都没有发生,甚至控制台也没有错误。

请注意,将context对象保留在Chunk类上并将其用于后续运行的注释代码将适用于 Word Online / Chrome,但不适用于 Windows 或 OSX Word

您的环境

  • 平台 [PC 桌面、Mac、iOS、Office Online]:Win11 桌面、OSX、Word Online(所有最新版本)
  • 宿主【Excel、Word、PowerPoint等】:Word
  • 办公版本号:最新
  • 操作系统:OSX/Win11
  • 浏览器(如果使用 Office Online):Chrome

代码:

import * as React from 'react'
import { Container, ListGroup, ListGroupItem, Button, Label, Input, ButtonGroup, Row } from 'reactstrap'

class Chunk {
    range: Word.Range
    text: string
    // context: Word.RequestContext
    constructor(t: string, r: Word.Range, ctx: Word.RequestContext) {
        this.range = r
        this.text = t
        ctx.trackedObjects.add(r)
        r.track()
        // this.context = ctx
    }
    async select(ctx: Word.RequestContext) {
        console.log('select')
        this.range.select('Select')
        ctx.sync()
    }
}

const getChunks = async () => {
    return Word.run(async context => {
        let paragraphs = context.document.body.paragraphs.load()
        let wordRanges: Array<Word.RangeCollection> = []
        await context.sync()
        paragraphs.items.forEach(paragraph => {
            const ranges = paragraph.getTextRanges([' ', ',', '.', ']', ')'], true)
            ranges.load('text')
            wordRanges.push(ranges)
        })
        await context.sync()
        let chunks: Chunk[] = []
        wordRanges.forEach(ranges => ranges.items.forEach(range => {
            chunks.push(new Chunk(range.text, range, context))
        }))
        await context.sync()
        return chunks
    })

}

interface ChunkControlProps { chunk: Chunk; onSelect: (e: React.MouseEvent<HTMLElement>) => void }
export const ChunkControl: React.SFC<ChunkControlProps> = ({ chunk, onSelect}) => {
    return (
        <div style={{marginLeft: '0.5em'}}><a href='#' onClick={onSelect}>{chunk.text}</a></div>
    )
}
export class App extends React.Component<{title: string}, {chunks: Chunk[]}> {
    constructor(props, context) {
        super(props, context)
        this.state = { chunks: [] }
    }

    componentDidMount() { this.click() }

    click = async () => {
        const chunks = await getChunks()
        this.setState(prev => ({ ...prev, chunks: chunks }))
    }

    onSelectRange(chunk: Chunk) {
        return async (e: React.MouseEvent<HTMLElement>) => {
            e.preventDefault()
            Word.run(ctx => chunk.select(ctx))
        }
    }

    render() {
        return (
            <Container fluid={true}>
                <Button color='primary' size='sm' block className='ms-welcome__action' onClick={this.click}>Find Chunks</Button>
                <hr/>
                <ListGroup>
                    {this.state.chunks.map((chunk, idx) => (
                        <ListGroupItem key={idx}>
                            <ChunkControl  onSelect={this.onSelectRange(chunk)} chunk={chunk}/>
                        </ListGroupItem>
                    ))}
                </ListGroup>
            </Container>
        )
    };
};
Run Code Online (Sandbox Code Playgroud)

适用于 WordOnline 但不适用于 Windows 或 OSX 的版本:

(省略了上面的重复代码)

 class Chunk {
    range: Word.Range
    text: string
    context: Word.RequestContext
    constructor(t: string, r: Word.Range, ctx: Word.RequestContext) {
        this.range = r
        this.text = t
        this.context = ctx
    }
    async select() {
        this.range.select('Select')
        ctx.sync()
    }
}

const getChunks = async () => {
    return Word.run(async context => {
        ...
    })

}

...

export class App extends React.Component<{title: string}, {chunks: Chunk[]}> {
    constructor(props, context) {
        super(props, context)
        this.state = { chunks: [] }
    }

    componentDidMount() { this.click() }

    click = async () => {
        const chunks = await getChunks()
        this.setState(prev => ({ ...prev, chunks: chunks }))
    }

    onSelectRange(chunk: Chunk) {
        return async (e: React.MouseEvent<HTMLElement>) => {
            e.preventDefault()
            chunk.select()
        }
    }

    render() {
        return (
            <Container fluid={true}>
                <Button color='primary' size='sm' block className='ms-welcome__action' onClick={this.click}>Find Chunks</Button>
                <hr/>
                <ListGroup>
                    {this.state.chunks.map((chunk, idx) => (
                        <ListGroupItem key={idx}>
                            <ChunkControl  onSelect={this.onSelectRange(chunk)} chunk={chunk}/>
                        </ListGroupItem>
                    ))}
                </ListGroup>
            </Container>
        )
    };
};
Run Code Online (Sandbox Code Playgroud)

Mic*_*oft 9

就一般模式而言:诀窍是Word.run像往常一样执行 a ,但不是让它创建新的匿名请求上下文,而是使用某些现有对象的上下文恢复运行。要继续使用现有上下文,您只需使用 Word.run 中可用的函数重载之一;即,将对象(或对象数组)作为第一个参数,将批处理作为第二个参数的重载:

代码示例

请注意,为了能够跨不同的run-s使用 Range 对象,您需要range.track()在第一次运行完成之前调用以延长其生命周期;你应该在某个时候使用range.untrack().

上面的文本和代码示例来自我使用 Office.js 构建 Office 加载项一书。那里还有更多信息。特别粘贴在一个部分:

当您忘记传入 Range 对象时会发生什么

本书中您可能会发现有用的相关部分:

总目录

至于你对 Online 和 Windows/Mac 之间行为差异的观察——这很有趣,听起来像是一个应该调查的错误。您介意在https://github.com/OfficeDev/office-js/issues以最少的重现为该特定方面提交错误吗?(很抱歉让你来来回回,我知道你已经在https://github.com/OfficeDev/office-js/issues/68;但我想分离出概念性问题,这实际上只是一个问题(和文档问题),而不是行为差异中可能出现的错误)。

最好的事物!

~迈克尔