Using SignalR to communicate between an App for Office and Popups
Apps for Office are a powerful and flexible way to extend Office across all the new Office form factors (browser, PC, phone). Apps for Office come in many sizes/shapes (Mail Apps, Compose Apps, Task Pane Apps, Content Apps). Although users can resize apps for Office, they will typically launch in a default dimension that developers should design around. Often this doesn’t provide enough screen real estate to display everything the app needs (ex: task pane apps default to 320px wide). A good example is an OAuth flow against a 3rd party, where the app has no control over design. For these scenarios, it might be appropriate to leverage popups (in general popups should be avoided). The challenge is that apps run in an isolation that prevents popups from communicating back into them. This post outlines the use of SignalR to solve this communication challenge.
[View:https://www.youtube.com/watch?v=FGsa2J5qmcE]
What is SignalR
SignalR is an ASP.NET technology that enables near real-time communication between servers and web browsers. It uses “Hubs” on the server that can broadcast data over WebSocket to all or specific web browsers that are connected to the hub. WebSockets enable SignalR to push data to web browser (as opposed to a web browser polling for data). Here is the SignalR Hub for the sample app...it simply accepts a message and sends it to the specified client ID.
SignalR Hub for sending messages
public class PopupCommunicationHub : Hub{ public void Initialize() { } public void SendMessage(string clientID, string message) { //send the message to the specific client passed in Clients.Client(clientID).sendMessage(message); }} |
How it Works
When a web browser establishes a connection to a SignalR hub, it is assigned a unique client ID (a GUID). The Office app and its popup(s) will all have their own unique client IDs. SignalR can push messages through the hub to all or specific client IDs. We can enable app-to-popup communication by making each aware of the others client ID. First we’ll pass the client ID of the parent to the popup via a URL parameter on the popup.
Passing the SignalR client ID of the app to the popup
// Start the connection.$.connection.hub.start().done(function () { hub.server.initialize(); //get the parentId off the hub parentId = $.connection.hub.id; //wire the event to launch popup (passing the parentId) $("#btnLaunchPopup").click(function () { //pass the parentId to the popup via url parameter window.open("/Home/PopupWindow?parentId=" + parentId, "", "width=850, height=600, scrollbars=0, toolbar=0, menubar=0, resizable=0, status=0, titlebar=0"); $("#authorizeModal").modal("show"); $("#btnLaunchPopup").html("Waiting on popup handshake"); $("#btnLaunchPopup").attr("disabled", "disabled"); }); //wire the send message $("#btnSend").click(function () { if (popupId != null) { //send the message over the hub hub.server.sendMessage(popupId, $("#txtMessage").val()); $("#txtMessage").val(""); } });}); |
The popup can read the app’s client ID off the URL and then send its own client ID as the first message to the parent (once the hub connection is setup).
Logic for the popup to read the client ID of app and send app its own client ID
var parentId = null, popupId = null;$(document).ready(function () { //utility function to get parameter from query string var getQueryStringParameter = function (urlParameterKey) { var params = document.URL.split('?')[1].split('&'); var strParams = ''; for (var i = 0; i < params.length; i = i + 1) { var singleParam = params[i].split('='); if (singleParam[0] == urlParameterKey) return singleParam[1]; } } //get the parentId off the url parameters parentId = decodeURIComponent(getQueryStringParameter('parentId')).split('#')[0]; //setup signalR hub var hub = $.connection.popupCommunicationHub; // Create a function that the hub can call to broadcast messages hub.client.sendMessage = function (message) { $("#theList").append($("<li class='list-group-item'>" + message + "</li>")); }; // Start the connection. $.connection.hub.start().done(function () { hub.server.initialize(); //get the popupId off the hub and send to the parent popupId = $.connection.hub.id; hub.server.sendMessage(parentId, popupId); //initialize the textbox $("#txtMessage").removeAttr("disabled"); //wire the send message $("#btnSend").click(function () { //send the message over the hub hub.server.sendMessage(parentId, $("#txtMessage").val()); $("#txtMessage").val(""); }); });}); |
The app will expect the popup client ID as the first message it receives from the hub. At this point, the app and the popup know the client IDs of the other. These client IDs are like the browser windows phone number for communication. Messages can be sent to specific client IDs and get pushed through the hub in near real time.
Logic in app to treat first message as the client ID of the popup
// Create a function that the hub can call to broadcast messageshub.client.sendMessage = function (message) { //first message should be the popupId if (popupId == null) { popupId = message; $("#init").hide(); $("#send").show(); } else { $("#theList").append($("<li class='list-group-item'>" + message + "</li>")); }}; |
Conclusion
Although popups should be avoided with apps for Office, it is sometime unavoidable. In those scenarios, SignalR give apps a better user experience. You can download the completed solution outlined in the video and post below.
Download the Solution: https://1drv.ms/1EOhtyl