Modern Single Page Applications (SPAs) run in the browser as a single HTML page and use JavaScript to dynamically load content and provide the functionality of the application without having to reload the entire browser window as the user is interacting with the application. To support navigating SPAs, a commonly used technique is to perform navigation (or routing as it is called in the Angular world) based on URL fragments.
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!
Thanks for bringing this problem forward.
Did you eventually code around this issue? I am concerned that such workaround code might break if and when Microsoft ever fixes this issue by no longer mangling the URLs.
LikeLiked by 1 person
I believe this is a bug in Outlook Web Access for Exchange 2016. It does not happen in Office 356 which, instead of packing that crap on the URL, puts it into a separate name=”…” property on the iframe.
In my case, even loading the root page was breaking because of this issue.
My workaround for that is to make sure my last Angular route is a catchall that redirects to my main component. For example:
[
{path: “”, component: HomeComponent},
// … other routes…
{path: “**”, redirectTo: HomeComponent}
]
This way, the initial frame load with all the extra params and anchors routes to my starting point, and then from there, Angular is in control or URLs and routing and it’s all fine. Perhaps this was obvious to the original author, as it sounds like his app worked when using just index.html without an anchor…but it wasn’t obvious to me until I landed here…so maybe this helps someone out…
As for the final question – how to handle this when you want to pass in an anchor and have it use your routes…I would probably approach it like this:
1) Make that final route go to a special component just for this case.
2) Make your manifest link prefix (and/or suffix) the route with something unique and detectable. So…say I use my initials for that… instead of going to “index.html#newitem” I would go to “index.html#swb-newitem-swb”.
3) Make that catchall component parse out /swb-(.*)-swb/ from the location when it gets called, and then redirect to the route that you pulled out.
YMMV… curious to hear if you (Charles) or anyone else has a better solution…
LikeLiked by 1 person
Hi Scott, thanks for leaving such a detailed comment and proposing a solution. I quite like your solution, when I first wrote that article the way I thought the problem could be solved was to provide different HTML pages for the different routes you wanted to come in on from your addin. So lets say you have 2 buttons in Outlook (start app at route 1 & start app at route 2), here I was thinking to create 2 HTML pages and have the Outlook manifest point directly to those pages. Then on those initial pages perform a redirect to start your addin to the correct route without all the garbage parameters.
I think I prefer your solution as it keeps all the logic within the app.
Thanks again.
LikeLike