javascriptangular-ui-routerms-officeoffice-jshtml5mode

Adding office.js adds # in the url, then removes it


I have a mean-stack web site. Initially, views/index.html (having <ui-view></ui-view>) is the entry point of all the pages. It uses angular-ui-router and html5mode. Thus, https://localhost:3000/XXXX in a browser will remain the same (rather than adding #) and show the corresponding content based on the router.

Then, I want the server to serve also an Office add-in. I will need views/addin.html that contains office.js and another router, such that the site serves a set of urls https://localhost:3000/addin/XXXX, and I don't mind if it is html5mode or not (a url can contain # somewhere).

So here is the correpsonding of routes/index.js:

router.get('/addin/*', function (req, res) {
    console.log("router.get /addin/*");
    res.sendfile('./views/addin.html')
});

router.get('*', function(req, res) {
    console.log("router.get *");
    res.sendfile('./views/index.html');
});

And here is views/addin.html:

<html>
<head>
    <title>F-addin</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.2/angular-ui-router.js"></script>
    <!--<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>-->
    <base href="/" />
</head>
<body ng-app="addinF">
    addin content
    <ui-view ng-cloak></ui-view>
</body>
<script>
    var addinApp = angular.module('addinF', ['ui.router'])
    addinApp.config(['$stateProvider', '$locationProvider', function ($stateProvider, $locationProvider) {
        $stateProvider
            .state('addinNew', {
                url: '/addin/new',
                template: "new page"
            })
            .state('addinHome', {
                url: '/addin/home',
                template: "home page"
            })
        $locationProvider.html5Mode(true);
    }]);
</script>
</html>

When office.js is commented as above, https://localhost:3000/addin/new and https://localhost:3000/addin/home in a browser work well. However, when office.js is uncommented, there is an odd behavior: typing https://localhost:3000/addin/new in a browser will change to https://localhost:3000/#/addin/new, then change back to https://localhost:3000/addin/new. And we will see addin content new page appear once and disappear.

With office.js uncommented, if we load a manifest file with <SourceLocation DefaultValue="https://localhost:3000/addin/new"/>, we will see also in the task pane addin content new page appear once and disappear, and then Add-in Error Something went wrong and we couldn't start this add-in. Please try again later or contact your system administrator.

Without html5mode here, .../addin/new or .../addin/home in a browser will only show addin content; the add-in can be loaded, with only addin content too.

My goal is to make an add-in pointing to .../addin/new or .../addin/home work. office.js has to be loaded somewhere. I don't mind if it is html5mode (ie, the url has #), but the behavior in either way is odd or unsatisfactory. Does anyone know how to fix it or have a workaround?


Solution

  • Looking at the debug version of the office.js file:

    https://appsforoffice.microsoft.com/lib/1/hosted/office.debug.js

    You'll see that the window's history replaceState and pushState functions are set to null:

    window.history.replaceState = null;
    window.history.pushState = null;
    

    Which disables the ability to manipulate the browser's history and it seems it has Angular thinking the history API is not available, so Angular falls back to hashtag navigation.

    In the office.js minified version you can find these two lines by looking for:

    window.history.replaceState=e;window.history.pushState=e;
    

    You could remove these two lines of code to re-enable html5mode, but i'm not sure if the office.js plugin will keep working properly.