Office-JS API 使用 Fetch 从服务器获取数据,替换 Word 中的字段 - ItemNotFound 错误

Office-JS API with Fetch to get data from server, replace fields in Word - ItemNotFound error

我是 Office-JS 的新手 API,但我正在尝试开发一个 POC 来演示通过单击按钮将 Word 文档中的所有字段替换为从中检索到的相应数据的能力一个 API.

所以我开发了一个 API 作为 Azure 函数,我可以调用它,传递一个值,它将 return 字段名称和值作为 JSON 用于匹配传递值的记录。我已经使用 Postman 对此进行了测试。

现在我正在尝试让 Office-JS 工作。我从 VS-2022 模板开始,我已将其转换为 TypeScript。

我打算采用的方法是,当用户单击按钮时,应用程序将调用 API 并获取记录(数据)字段作为一组 name/value 对。

然后,我想遍历所有的(数据)字段 returned,并且对于每个(数据)字段名称,检查文档中是否有一个(文档)字段姓名;如果是这样,请将(文档)字段替换为(数据)字段的值。

此代码已成功找到搜索条件,并将替换排队,为 context.sync 调用做准备...但是在 context.sync 调用期间,报告错误“ItemNotFound ."

我正在遵循此处 https://docs.microsoft.com/en-us/office/dev/add-ins/concepts/correlated-objects-pattern“使用相关对象模式处理文档中的对象”部分中记录的推荐方法。

我的代码如下:

(function () {
    "use strict";

    var messageBanner;

    // The initialize function must be run each time a new page is loaded.
    Office.initialize = function (reason) {
        (window as any).Promise = OfficeExtension.Promise;
        $(document).ready(function () {
            // Initialize the notification mechanism and hide it
            var element = document.querySelector('.MessageBanner');
            messageBanner = new components.MessageBanner(element);
            messageBanner.hideBanner();
            OfficeExtension.config.extendedErrorLogging = true;

            // If not using Word 2016, use fallback logic.
            if (!Office.context.requirements.isSetSupported('WordApi', '1.1')) {
                $("#template-description").text("This sample displays the selected text.");
                $('#button-text').text("Display!");
                $('#button-desc').text("Display the selected text");

                $('#highlight-button').click(loadCaseData("08-CA-123456"));
                //$('#highlight-button').click(simpleTest());
                //$('#highlight-button').click(dataCompareTest("08-CA-123456"));
                return;
            }

            $("#template-description").text("This POC demonstrates template capabilities for NLRB within WORD.");
            $('#button-text').text("Merge Template!");
            $('#button-desc').text("Replaces fields with appropriate data.");

            // Add a click event handler for the highlight button.
            $('#highlight-button').click(loadCaseData("08-CA-123456"));
            //$('#highlight-button').click(simpleTest());
            //$('#highlight-button').click(dataCompareTest("08-CA-123456"));
        });
    };

    //CaseNum=08-CA-123456&DataType=1
    async function loadCaseData(caseNum) {
        Word.run(async (context) => {
            const 
                url = `[My API URL]`;

            fetch(url)
                .then(resp => resp.json())
                .then(resp => {
                    console.log('Server Response : ' + JSON.stringify(resp));

                    let fieldList = Object.entries(resp).map(([k, v]) => {
                        let fieldNameString = '«' + k + '»';
                        return { fieldName: fieldNameString, fieldValue: v };
                    });
                    console.log("Dataset in first part: " + JSON.stringify(fieldList));
                    return fieldList;
                })
                .then(async fieldList => await searchDocForFields(fieldList, context))
                .catch(errorHandler);
            return context.sync();
        });
    }

    async function simpleTest() {
        Word.run(async (context) => {

            let fieldList = [
                { fieldName: "«StartLogSeqNo»", fieldValue: "AAA5fgABbpgACw==" },
                { fieldName: "«EndLogSeqNo»", fieldValue: null },
                { fieldName: "«SeqVal»", fieldValue: "AAA5fgABbpgAAw==" },
                { fieldName: "«DmlOperation»", fieldValue: 4 },
                { fieldName: "«DmlUpdateFields»", fieldValue: "AAAAAAAAAUAgADg=" },
                { fieldName: "«CaseAssgnedDt»", fieldValue: "2011-07-22T17:12:19" },
                { fieldName: "«AsgnUsrExcldFlg»", fieldValue: "N" },
                { fieldName: "«BuId»", fieldValue: "0-R9NH" },
                { fieldName: "«CaseFiledDt»", fieldValue: "2006-03-09T00:00:00" },
                { fieldName: "«InquiryId»", fieldValue: "08-CA-123456" },
                { fieldName: "«ChgofccmReqFlg»", fieldValue: "N" },
                { fieldName: "«DisputeUnitCity»", fieldValue: "Cleveland" },
                { fieldName: "«CaseClasification»", fieldValue: "Unclassified" },
                { fieldName: "«CaseClosedDt»", fieldValue: null },
                { fieldName: "«Created»", fieldValue: "2010-10-07T15:32:29" },
                { fieldName: "«CreatedBy»", fieldValue: "0-1" },
                { fieldName: "«CrimeSubTypeCd»", fieldValue: null },
                { fieldName: "«TypeCd»", fieldValue: null },
                { fieldName: "«DbLastUpd»", fieldValue: "2019-10-20T17:17:14.31" },
                { fieldName: "«DbLastUpdSrc»", fieldValue: "ScriptingService_PreInvokeMethod" },
                { fieldName: "«CaseDescription»", fieldValue: null },
                { fieldName: "«LastUpd»", fieldValue: "2019-10-20T17:17:14" },
                { fieldName: "«LastUpdBy»", fieldValue: "1-CGA" },
                { fieldName: "«LocalSeqNum»", fieldValue: 1 },
                { fieldName: "«ModificationNum»", fieldValue: 11 },
                { fieldName: "«CaseName»", fieldValue: "This is the case name from the data" },
                { fieldName: "«ParCaseId»", fieldValue: null },
                { fieldName: "«PrAgencyId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrAgentId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrPostnId»", fieldValue: "1-5D2V9F       " },
                { fieldName: "«PrPrtnrId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrRepDnrmFlg»", fieldValue: "Y" },
                { fieldName: "«PrRepManlFlg»", fieldValue: "Y" },
                { fieldName: "«PrRepSysFlg»", fieldValue: "Y" },
                { fieldName: "«PrSgroupId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrSubjectId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrSuspctId»", fieldValue: "No Match Row Id" },
                { fieldName: "«IaCategory»", fieldValue: "2" },
                { fieldName: "«RewardExchangeDt»", fieldValue: "2010-10-07T11:09:02" },
                { fieldName: "«RowId»", fieldValue: "1-2DCA-1327" },
                { fieldName: "«CaseNumber»", fieldValue: "08-CA-123456" },
                { fieldName: "«CaseSource»", fieldValue: "Visit" },
                { fieldName: "«DisputeUnitState»", fieldValue: "OH" },
                { fieldName: "«CaseStatus»", fieldValue: "Open" },
                { fieldName: "«CaseSubType»", fieldValue: "CA" },
                { fieldName: "«CaseSubTypeCd»", fieldValue: null },
                { fieldName: "«TerritoryTypeCd»", fieldValue: "08" },
                { fieldName: "«ThreatLvlCd»", fieldValue: "Batch" },
                { fieldName: "«CaseType»", fieldValue: "C" },
                { fieldName: "«BlockedFlag»", fieldValue: null },
                { fieldName: "«CaseLongName»", fieldValue: "This is the case name from the data" },
                { fieldName: "«XCaseNumCi»", fieldValue: null },
                { fieldName: "«DojCaseType»", fieldValue: null },
                { fieldName: "«ElectionTargetDt»", fieldValue: null },
                { fieldName: "«HearingTargetDt»", fieldValue: null },
                { fieldName: "«MethodType»", fieldValue: null },
                { fieldName: "«XNameCi»", fieldValue: null },
                { fieldName: "«Num8a3Discriminatees»", fieldValue: null },
                { fieldName: "«Num8b2Discriminatees»", fieldValue: null },
                { fieldName: "«NumOfEmployees»", fieldValue: 146 },
                { fieldName: "«PostElectionSelfCertification»", fieldValue: null },
                { fieldName: "«Potential10j»", fieldValue: "N" },
                { fieldName: "«XPrPostnBrdId»", fieldValue: "1-4P8HN1" },
                { fieldName: "«XPrPostnSpvId»", fieldValue: "1-1RAQT2" },
                { fieldName: "«ElectionSelfCertification»", fieldValue: null },
                { fieldName: "«XTypeCdCi»", fieldValue: null },
                { fieldName: "«Moved2dh»", fieldValue: 1 },
                { fieldName: "«IdentityVal»", fieldValue: 5792070 },
                { fieldName: "«CdcRecordedFields»", fieldValue: null },
                { fieldName: "«NxgenTestCase»", fieldValue: "N" },
                { fieldName: "«InquiryChargePetition»", fieldValue: null },
                { fieldName: "«ChangeCaptureDatetime»", fieldValue: "2019-10-20T13:17:15.83" },
                { fieldName: "«RegionRecommendsPursuing10j»", fieldValue: "N" },
                { fieldName: "«SurrogateKey»", fieldValue: 1731295 }
            ]

            await searchDocForFields(fieldList, context);
        })
    }

    async function searchDocForFields(fieldList, context) {

        console.log("Dataset: " + JSON.stringify(fieldList));

        const allSearchResults = [];
        console.log('fieldList.Length = ' + fieldList.length);
        //    console.log("Dataset in second part: " + JSON.stringify(fieldList));
        for (let fieldCount = 0; fieldCount < fieldList.length; fieldCount++) {
            let options = Word.SearchOptions.newObject(context);
            options.matchWildCards = false;
            options.matchCase = true;
            options.matchWholeWord = true;
            // Check the document to see if this field from the database is found
            //        console.log("Checking for " + fieldList[fieldCount].fieldName + " to be replaced with " + fieldList[fieldCount].fieldValue);
            let searchResults = context.document.body.search(fieldList[fieldCount].fieldName, options);
            //Load all of the items that meet the search criteria
            searchResults.load('items');
            let correlatedSearchResult = {
                rangesMatchingFieldName: searchResults,
                fieldValue: fieldList[fieldCount].fieldValue
            }
            allSearchResults.push(correlatedSearchResult);
        }
        await context.sync();

        // Now we have all of the search results in the correlatedSearchResult array.
        for (let searchResultCount = 0; searchResultCount < allSearchResults.length; searchResultCount++) {
            let correlatedObject = allSearchResults[searchResultCount];
            console.log("Checking for search result " + searchResultCount + " of " + allSearchResults.length);
            for (let rangeCount = 0; rangeCount < correlatedObject.rangesMatchingFieldName.items.length; rangeCount++) {
                let targetRange = correlatedObject.rangesMatchingFieldName.items[rangeCount];
                let replacementValue = correlatedObject.fieldValue;
                targetRange.insertText(replacementValue, Word.InsertLocation.replace);
                console.log("Replacing found instance " + rangeCount + " of " + correlatedObject.rangesMatchingFieldName.items.length + " (" + JSON.stringify(targetRange) + ") with (" + replacementValue + ")");
            }
        }
        return await context.sync();
    }

    function dataCompareTest(caseNum) {
        Word.run((context) => {
            const
                url = `[My API URL]`;

            const codedFieldList = [
                { fieldName: "«StartLogSeqNo»", fieldValue: "AAA5fgABbpgACw==" },
                { fieldName: "«EndLogSeqNo»", fieldValue: null },
                { fieldName: "«SeqVal»", fieldValue: "AAA5fgABbpgAAw==" },
                { fieldName: "«DmlOperation»", fieldValue: 4 },
                { fieldName: "«DmlUpdateFields»", fieldValue: "AAAAAAAAAUAgADg=" },
                { fieldName: "«CaseAssgnedDt»", fieldValue: "2011-07-22T17:12:19" },
                { fieldName: "«AsgnUsrExcldFlg»", fieldValue: "N" },
                { fieldName: "«BuId»", fieldValue: "0-R9NH" },
                { fieldName: "«CaseFiledDt»", fieldValue: "2006-03-09T00:00:00" },
                { fieldName: "«InquiryId»", fieldValue: "08-CA-123456" },
                { fieldName: "«ChgofccmReqFlg»", fieldValue: "N" },
                { fieldName: "«DisputeUnitCity»", fieldValue: "Cleveland" },
                { fieldName: "«CaseClasification»", fieldValue: "Unclassified" },
                { fieldName: "«CaseClosedDt»", fieldValue: null },
                { fieldName: "«Created»", fieldValue: "2010-10-07T15:32:29" },
                { fieldName: "«CreatedBy»", fieldValue: "0-1" },
                { fieldName: "«CrimeSubTypeCd»", fieldValue: null },
                { fieldName: "«TypeCd»", fieldValue: null },
                { fieldName: "«DbLastUpd»", fieldValue: "2019-10-20T17:17:14.31" },
                { fieldName: "«DbLastUpdSrc»", fieldValue: "ScriptingService_PreInvokeMethod" },
                { fieldName: "«CaseDescription»", fieldValue: null },
                { fieldName: "«LastUpd»", fieldValue: "2019-10-20T17:17:14" },
                { fieldName: "«LastUpdBy»", fieldValue: "1-CGA" },
                { fieldName: "«LocalSeqNum»", fieldValue: 1 },
                { fieldName: "«ModificationNum»", fieldValue: 11 },
                { fieldName: "«CaseName»", fieldValue: "This is the case name from the data" },
                { fieldName: "«ParCaseId»", fieldValue: null },
                { fieldName: "«PrAgencyId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrAgentId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrPostnId»", fieldValue: "1-5D2V9F       " },
                { fieldName: "«PrPrtnrId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrRepDnrmFlg»", fieldValue: "Y" },
                { fieldName: "«PrRepManlFlg»", fieldValue: "Y" },
                { fieldName: "«PrRepSysFlg»", fieldValue: "Y" },
                { fieldName: "«PrSgroupId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrSubjectId»", fieldValue: "No Match Row Id" },
                { fieldName: "«PrSuspctId»", fieldValue: "No Match Row Id" },
                { fieldName: "«IaCategory»", fieldValue: "2" },
                { fieldName: "«RewardExchangeDt»", fieldValue: "2010-10-07T11:09:02" },
                { fieldName: "«RowId»", fieldValue: "1-2DCA-1327" },
                { fieldName: "«CaseNumber»", fieldValue: "08-CA-123456" },
                { fieldName: "«CaseSource»", fieldValue: "Visit" },
                { fieldName: "«DisputeUnitState»", fieldValue: "OH" },
                { fieldName: "«CaseStatus»", fieldValue: "Open" },
                { fieldName: "«CaseSubType»", fieldValue: "CA" },
                { fieldName: "«CaseSubTypeCd»", fieldValue: null },
                { fieldName: "«TerritoryTypeCd»", fieldValue: "08" },
                { fieldName: "«ThreatLvlCd»", fieldValue: "Batch" },
                { fieldName: "«CaseType»", fieldValue: "C" },
                { fieldName: "«BlockedFlag»", fieldValue: null },
                { fieldName: "«CaseLongName»", fieldValue: "This is the case name from the data" },
                { fieldName: "«XCaseNumCi»", fieldValue: null },
                { fieldName: "«DojCaseType»", fieldValue: null },
                { fieldName: "«ElectionTargetDt»", fieldValue: null },
                { fieldName: "«HearingTargetDt»", fieldValue: null },
                { fieldName: "«MethodType»", fieldValue: null },
                { fieldName: "«XNameCi»", fieldValue: null },
                { fieldName: "«Num8a3Discriminatees»", fieldValue: null },
                { fieldName: "«Num8b2Discriminatees»", fieldValue: null },
                { fieldName: "«NumOfEmployees»", fieldValue: 146 },
                { fieldName: "«PostElectionSelfCertification»", fieldValue: null },
                { fieldName: "«Potential10j»", fieldValue: "N" },
                { fieldName: "«XPrPostnBrdId»", fieldValue: "1-4P8HN1" },
                { fieldName: "«XPrPostnSpvId»", fieldValue: "1-1RAQT2" },
                { fieldName: "«ElectionSelfCertification»", fieldValue: null },
                { fieldName: "«XTypeCdCi»", fieldValue: null },
                { fieldName: "«Moved2dh»", fieldValue: 1 },
                { fieldName: "«IdentityVal»", fieldValue: 5792070 },
                { fieldName: "«CdcRecordedFields»", fieldValue: null },
                { fieldName: "«NxgenTestCase»", fieldValue: "N" },
                { fieldName: "«InquiryChargePetition»", fieldValue: null },
                { fieldName: "«ChangeCaptureDatetime»", fieldValue: "2019-10-20T13:17:15.83" },
                { fieldName: "«RegionRecommendsPursuing10j»", fieldValue: "N" },
                { fieldName: "«SurrogateKey»", fieldValue: 1731295 }
            ]

            fetch(url)
                .then(resp => resp.json())
                .then(resp => {
                    console.log('Server Response : ' + JSON.stringify(resp));

                    let fetchFieldList = Object.entries(resp).map(([k, v]) => {
                        let fieldNameString = '«' + k + '»';
                        return { fieldName: fieldNameString, fieldValue: v };
                    });
                    console.log("Dataset in first part: " + JSON.stringify(fetchFieldList));
                    return fetchFieldList;
                })
                .then(fieldList => {
                    console.log('Comparing datasets');

                    const jsonFieldList = JSON.stringify(fieldList);
                    const jsonCodedFieldList = JSON.stringify(codedFieldList);
                    const same = jsonFieldList === jsonCodedFieldList;
                    console.log("Are the arrays the same? " + same);
                })
                .catch(errorHandler);
            return context.sync();
        });
    }


    //$$(Helper function for treating errors, $loc_script_taskpane_home_js_comment34$)$$
    function errorHandler(error) {
        // $$(Always be sure to catch any accumulated errors that bubble up from the Word.run execution., $loc_script_taskpane_home_js_comment35$)$$
        showNotification("Error:", error);
        console.log("Error: " + error);
        if (error instanceof OfficeExtension.Error) {
            console.log("Debug info: " + JSON.stringify(error.debugInfo));
        }
    }

    // Helper function for displaying notifications
    function showNotification(header, content) {
        $("#notification-header").text(header);
        $("#notification-body").text(content);
        messageBanner.showBanner();
        messageBanner.toggleExpansion();
    }
})();

样本 JSON return 来自我的数据 API:

{
    "CaseAssgnedDt": "2011-07-22T17:12:19",
    "AsgnUsrExcldFlg": "N",
    "BuId": "0-R9NH",
    "CaseFiledDt": "2006-03-09T00:00:00",
    "InquiryId": "08-CA-123456",
    "ChgofccmReqFlg": "N",
    "DisputeUnitCity": "Cleveland",
    "CaseClasification": "Unclassified",
    "CaseClosedDt": null,
    "Created": "2010-10-07T15:32:29",
    "CreatedBy": "0-1",
    "CrimeSubTypeCd": null,
    "TypeCd": null,
    "DbLastUpd": "2019-10-20T17:17:14.31",
    "DbLastUpdSrc": "ScriptingService_PreInvokeMethod",
    "CaseDescription": null,
    "LastUpd": "2019-10-20T17:17:14",
    "LastUpdBy": "1-CGA",
    "LocalSeqNum": 1,
    "ModificationNum": 11,
    "CaseName": "This is the case name from the data",
    "ParCaseId": null,
    "PrAgencyId": "No Match Row Id",
    "PrAgentId": "No Match Row Id",
    "PrPostnId": "1-5D2V9F       ",
    "PrPrtnrId": "No Match Row Id",
    "PrRepDnrmFlg": "Y",
    "PrRepManlFlg": "Y",
    "PrRepSysFlg": "Y",
    "PrSgroupId": "No Match Row Id",
    "PrSubjectId": "No Match Row Id",
    "PrSuspctId": "No Match Row Id",
    "IaCategory": "2",
    "RewardExchangeDt": "2010-10-07T11:09:02",
    "RowId": "1-2DCA-1327",
    "CaseNumber": "08-CA-123456",
    "CaseSource": "Visit",
    "DisputeUnitState": "OH",
    "CaseStatus": "Open",
    "CaseSubType": "CA",
    "CaseSubTypeCd": null,
    "TerritoryTypeCd": "08",
    "ThreatLvlCd": "Batch",
    "CaseType": "C",
    "BlockedFlag": null,
    "CaseLongName": "This is the case name from the data",
    "XCaseNumCi": null,
    "DojCaseType": null,
    "ElectionTargetDt": null,
    "HearingTargetDt": null,
    "MethodType": null,
    "XNameCi": null,
    "Num8a3Discriminatees": null,
    "Num8b2Discriminatees": null,
    "NumOfEmployees": 146,
    "PostElectionSelfCertification": null,
    "Potential10j": "N",
    "XPrPostnBrdId": "1-4P8HN1",
    "XPrPostnSpvId": "1-1RAQT2",
    "ElectionSelfCertification": null,
    "XTypeCdCi": null,
    "Moved2dh": 1,
    "IdentityVal": 5792070,
    "CdcRecordedFields": null,
    "NxgenTestCase": "N",
    "InquiryChargePetition": null,
    "ChangeCaptureDatetime": "2019-10-20T13:17:15.83",
    "RegionRecommendsPursuing10j": "N",
    "SurrogateKey": 1731295
}

非常感谢您提供的一切帮助,谢谢。

谢谢。

-- 更新: 根据 Rick Kirkham 的建议,我创建了一个新函数(“simpleTest”,现在包含在上面的代码中)来简化和验证该方法,并且惊讶地发现它有效(尽管我敢打赌 Rick 并不感到惊讶,哈哈).

我还添加了一个函数“dataCompare”,也在上面的代码中),它手动加载一个数据集,另一个使用 Fetch 调用,然后比较两者,它响应两个数组是一样。

鉴于数据是相同的,而且 loadCaseData 和 simpleTest 函数都使用相同的 searchDocForFields 函数来执行搜索和替换,所以我很茫然,为什么 simpleTest 函数会工作,以及loadCaseData 函数因 ItemNotFound 错误而失败?

谢谢。 马克.

最终,这一切都是由于缺少单个等待引起的...请注意下面带有 FETCH 语句的行,该语句之前没有 AWAIT。

    async function loadCaseData(caseNum) {
        Word.run(async (context) => {
            const ul = document.getElementById('caseData'),
                url = `[URL to my API]`;

            const createNode = element => { return document.createElement(element); };
            const append = (parent, el) => { return parent.appendChild(el); };

/*----->*/  await fetch(url)
                .then(resp => resp.json())
                .then(resp => {
                    console.log('Server Response : ' + JSON.stringify(resp));

                    let fieldList = Object.entries(resp).map(([k, v]) => {
                        let fieldNameString = '«' + k + '»';
                        return { fieldName: fieldNameString, fieldValue: v };
                    });
                    //console.log("Dataset in first part: " + JSON.stringify(fieldList));
                    return fieldList;
                })
                .then(async fieldList => await searchDocForFields(fieldList, context))
                .catch(errorHandler);
            return context.sync();
        });
    }

    async function newLoadCaseData(caseNum) {
        Word.run(async (context) => {
            const 
                url = `[URL to my API]`;

            let resp = await (await fetch(url)).json();
            //let data = resp.json();
            console.log('Server Response : ' + JSON.stringify(resp));

            let fieldList = Object.entries(resp).map(([k, v]) => {
                let fieldNameString = '«' + k + '»';
                return { fieldName: fieldNameString, fieldValue: v };
            });
            console.log("Dataset in first part: " + JSON.stringify(fieldList));

            await searchDocForFields(fieldList, context);
        });
    }

我通过尝试进一步简化我的代码发现了这一点,因此创建了同样显示的“newLoadCaseData”函数。这个版本写FETCH语句的时候,VS2022给我插了个await,让我大开眼界。

现在可以使用了,我希望我的研究和 mistakes/fixes 也能帮助到其他人。

尽情享受吧!

马克.