Communication between Office Web Add-ins


Sometimes you have multiple add-ins and you need to facilitate communication between them. For example, a common scenario I have heard is that you have:

  • A Content Add-in that displays something like a graph or an organization chart.
  • A Taskpane app that allows you to manipulate settings, upload and download data from a backend web service.

You need to be able to facilitate communication between the two so that when updates happen to one add-in, the other receives those updates. I recently worked on a proof of concept that helped prove how this can be done.

The solution is to use the Document as a communication medium. In the particular case we used CustomXMLParts in the document. Here is how it would work:

  • One add-in would need to send an update to the other, so it would write a CustomXMLPart with a specific namespace and a “context” (basically, I am the TaskPane communicating) to the document.
  • Both add-ins will have a window.setInterval() thread running to check the documents for CustomXMLParts in that given namespace.
  • The timer on the Content Add-in would fire, find the new customXMLPart from the taskpane, read the contents and then update itself as needed and finally, delete the CustomXMLPart.

Here is the code for the Content Add-in to look for the message from the TaskPane:

Office.initialize = function(reason) {
// background thread checker
window.setInterval(() => { checkForPart(); }, 1000);
}
const ns = "http://pfe.microsoft.com/excelpoc/1.0";
const xml = "<message xmlns='http://pfe.microsoft.com/excelpoc/1.0'>&quot; +
"<sentby>[who]</sentby>" +
"<info>[data]</info>" +
"</message>";
const from_tp = "TASKPANE ADD-IN";
function checkForPart() {
Excel.run(function(context) {
/**@type {Excel.CustomXmlPartScopedCollection} */
var customXmlParts = context.workbook.customXmlParts.getByNamespace(ns);
customXmlParts.load();
return context.sync().then(function () {
if(customXmlParts.items.length > 0) {
/**@type {OfficeExtension.ClientResult<string>} */
var xmlData = customXmlParts.items[0].getXml();
context.sync().then(function() {
/**@type {DOMParser} */
var parser = new window.DOMParser();
/**@type {Document} */
var xmlDoc = parser.parseFromString(xmlData.value, "text/xml");
/**@type {Element} */
var who = xmlDoc.getElementsByTagName("sentby")[0];
/**@type {Element} */
var data = xmlDoc.getElementsByTagName("info")[0];
document.getElementById("message").innerText = who.innerHTML;
if(who.innerHTML == from_tp) {
// write tot he pane
var dt = new Date();
var currentTime = pad(dt.getHours(),2) + ":" + pad(dt.getMinutes(),2) + ":" + pad(dt.getSeconds(),2);
// update a DIV on the page
document.getElementById("message").innerHTML = "<p>Message sent on " +
currentTime +
" by " + who.innerHTML +
" and the message is " + data.innerHTML;
// now we delete the part
customXmlParts.items[0].delete();
return context.sync();
}
});
}
});
});
}

Next, here is the code in the Task Pane Add-in that will send the message for the content add-in to read:

const ns = "http://pfe.microsoft.com/excelpoc/1.0&quot;;
const xml = "<message xmlns='http://pfe.microsoft.com/excelpoc/1.0'>&quot; +
"<sentby>[who]</sentby>" +
"<info>[data]</info>" +
"</message>";
const from_tp = "TASKPANE ADD-IN";
function sendMessage() {
Excel.run(function(context) {
var data = xml.replace("[who]", from_tp).replace("[data]", "This message is coming from the taskpane.");
const customXmlPart = context.workbook.customXmlParts.add(data);
customXmlPart.load();
return context.sync().then();
});
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s