'Office-JS API with Fetch to get data from server, replace fields in Word
I am new to the Office-JS API, but trying to develop a POC to demonstrate the ability to, with the click of a button, replace all fields in a Word document with corresponding data retrieved from an API.
So I have developed an API as an Azure Function, which I can call, passing a value, and it will return the field names and values as JSON for the record that matches the passed value. I have tested this already using Postman.
Now I am trying to get the Office-JS piece working. I started with the VS-2022 template.
My intended approach is that when the user clicks the button, the application will call the API and obtain the record (data) fields as a set of name/value pairs.
Then, I want loop through all of the (data) fields returned, and for each (data) field name, check to see if there is a (document) field in the document by that name; if so, replace the (document) field with the (data) field's value.
In the end, I realize it would likely be more efficient to loop the other way (loop through the doc fields, and then get the (data) field's value, etc.), but I figure I can tweak this once I get it working.
I also realize it's bad practice to perform a context.sync() within a loop, but again - I can clean that up once I get it working.
Right now, my code is reporting "Error: InvalidRequestContext: Cannot use the object across different request contexts."
My code follows:
(function () {
"use strict";
var messageBanner;
// The initialize function must be run each time a new page is loaded.
Office.initialize = function (reason) {
$(document).ready(function () {
// Initialize the notification mechanism and hide it
var element = document.querySelector('.MessageBanner');
messageBanner = new components.MessageBanner(element);
messageBanner.hideBanner();
// 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(displaySelectedText);
return;
}
$("#template-description").text("This POC demonstrates template capabilities for NLRB within WORD.");
$('#button-text').text("Merge Template!");
$('#button-desc').text("Replaces fields with approprate data.");
// Add a click event handler for the highlight button.
$('#highlight-button').click(loadCaseData("08-CA-036441"));
});
};
//CaseNum=08-CA-036441&DataType=1
function loadCaseData(caseNum) {
Word.run((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); };
fetch(url)
.then(response => { return response.json(); })
.then(json => {
let caseDataResult = json;
// Identify fields in the document
// Loop through case data for field substitution
var range = context.document.body;
context.load(range, 'text');
var searchResults;
for (let fieldName in caseDataResult) {
var searchFieldName = "«" + fieldName + "»";
let fieldValue = caseDataResult[fieldName];
return context.sync()
.then(function () {
// Queue a search command.
searchResults = range.search(searchFieldName, { matchCase: true, matchWholeWord: true });
// Queue a commmand to load the font property of the results.
context.load(searchResults, 'text');
})
.then(context.sync)
.then(function () {
var replaceCount = searchResults.items.length;
if (replaceCount && replaceCount >= 1) {
for (var replaceItem = 0; replaceItem < replaceCount; replaceItem++) {
searchResults[replaceItem].insertText(fieldValue,
Word.InsertLocation.replace);
}
}
})
.then(context.sync);
}
return context.sync();
})
.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();
}
})();
And the sample JSON returned from my data API:
{
"CaseAssgnedDt": "2011-07-22T17:12:19",
"AsgnUsrExcldFlg": "N",
"BuId": "0-R9NH",
"CaseFiledDt": "2006-03-09T00:00:00",
"InquiryId": "08-CA-036441",
"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": "Sample test case name",
"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-036441",
"CaseSource": "Visit",
"DisputeUnitState": "OH",
"CaseStatus": "Open",
"CaseSubType": "CA",
"CaseSubTypeCd": null,
"TerritoryTypeCd": "08",
"ThreatLvlCd": "Batch",
"CaseType": "C",
"BlockedFlag": null,
"CaseLongName": "Long sample test case name",
"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
}
I appreciate any and all help to get this working, please.
Thank you.
Solution 1:[1]
This is another common symptom of nested calls of context.sync. Fix the nesting first. Also, performance is better when you push filtering logic as close to the data source as possible. Consider designing the Azure Function to take in a list of fields and send back only data for matching names. Then your client side code is greatly simplified because you can assume there is a matching field for every data record that is returned.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Rick Kirkham |
