When developing add-ins for Office you are often serving the add-in from a local web server on a URL using the host name “localhost”. Office add-ins also require the web server to use SSL to serve the resources for the add-in. The Chrome security implementation will fire off a security error under most common development scenarios. This is when the domain of the SSL certificate does not match “localhost”.
You will see this problem manifest itself by causing your add-in to not start and show an error stating:
“Add-in Error Something went wrong and we couldn’t start this add-in. Please try again later or contact your system administrator”
If you have the Developer Tools window open in Chrome you will see error messages getting output with the text:
There is a relatively easy workaround to this problem that you can implement on your development machine to allow Chrome to bypass this certificate check on URLs served from “localhost”.
Type the following into the Chrome browser URL bar:
Enable the option:
“Allow invalid certificates for resources loaded from localhost. Mac, Windows, Linux, Chrome OS, Android
Allows requests to localhost over HTTPS even when an invalid certificate is presented.”
After making this change you will need to restart Chrome.
The basic premise of how this works is that the URL that you see in the browser window always refers to the same HTML page hosting your SPA e.g. http://myserver/spa-app/index.html
As the user navigates around the application this is done using URL fragments (the bit after the #) e.g. http://myserver/spa-app/index.html#configurationpage or http://myserver/spa-app/index.html#customerspage
This allows the browser to not go back to the server to request a page refresh (because we are always on the same page http://myserver/spa-app/index.html) but the SPA can react to the change of URL by reading the Fragment of the URL and route the user to the correct area of the app. The browser history also keeps track of the Fragment URL so this can provide a nice navigation experience.
That was a very basic explanation and I suggest reading this good primer on Fragment URLs (or hashbangs as they are sometimes referred).
So this leads us to Outlook add-ins and the problem I’ve encountered. Lets illustrate this with an example so that the use case becomes clear.
Imagine we have a simple SPA that shows a To Do list. The main screen of the app (http://myserver/spa-app/index.html) just shows the To Do list. There is also a second screen in the app for creating new To Do items(http://myserver/spa-app/index.html#newitem).
In the Outlook add-in manifest you provide a URL to your page that Outlook will load up in response to the user activating your app. The Microsoft preferred way of triggering this in Outlook is via Commands that appear as buttons in the Outlook Ribbon (in the desktop version of Outlook). If we create such a Ribbon button and specify the URL of the main screen of the app (http://myserver/spa-app/index.html) everything works just fine. Within the app itself, it can navigate off to http://myserver/spa-app/index.html#newitem to show the screen to create a new item. But what if we want to provide Outlook Ribbon buttons that streamline the process and let the user go straight to creating a new to do item rather than first having to open the app, then navigate within the app to create the item? Having the main functions of you app accessible as Ribbon buttons in Outlook is a huge time saver for users.
So what happens when we try to use the new item URL Fragment behind a Ribbon button?
If we specify a URL of http://myserver/spa-app/index.html#newitem in the add-in manifest, the following is the URL that Outlook actually launches the add-in with:
Obviously this is going to wreak havoc with your SPA. The original URL Fragment #newitem looks to be encoded in the resulting URL as “&_serializer_version=1newitem” although how to reliably detect and extract this and then do the correct routing within your SPA is challenging!
I have simple Angular 2 Office Addin and attempting to use the Angular 2 Router to route between two components. My two components are called ViewOne and ViewTwo.
Here’s what the UI for the Office Addin looks like:
The error message text is:
EXCEPTION: Error: Uncaught (in promise): TypeError: this_history.pushState is not a function
The same page displays without any error if it is not running as an Office Addin (rather if I just run the same router code on a standalone web page).
My best guess is that this error is due to the Office Addin framework and the fact that the Angular 2 app is running inside a sandbox iframe. I have tried running the same Angular app in a sandbox iframe on an otherwise generic html page however and I can’t reproduce the error so I think it is unique to something within the Office Addin framework.
This particular error has to do with the Angular 2 app trying to push the URL change to the web browsers history (to support back/forward navigation). In an Office Addin this doesn’t really make much sense as the Addin isn’t in control of the whole page so we wouldn’t want the Addin taking over the browsers URL history anyway.
In order to stop the Angular 2 router trying to make this call to the browser you can use a custom location strategy. In my case I was already using the HashLocationStrategy (rather than the default HTML5 routing strategy).
I went to the Angular 2 GitHub repo and found the source code for the HashLocationStrategy and created a new class in my Angular 2 app called CustomHashLocationStrategy. I just dumped all the source code into the new file, changed the name of the class and removed the two lines of code that try to update the web browsers history as shown below.
Now when bootstrapping my Angular 2 app I use my new CustomHashLocationStrategy instead of the HashLocationStrategy. Here’s what that change looks like in code.
Before (click for full size image):
After (click for full size image):
After this change I can now navigate between the 2 routes without any errors being thrown to the console.
The code shown in this article in the Angular 2 Router in RC1. I also had the same issue using the “Router-Deprecated” in RC1, the same solutions worked for me using the deprecated router.
I also tested that this fix worked across Chrome, IE, Edge and Windows Desktop Office Client.