Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Router with Office.js: router tries using push state even tho it's reset manually by the office js script #3154

Closed
Mafii opened this issue Mar 23, 2020 · 9 comments

Comments

@Mafii
Copy link

Mafii commented Mar 23, 2020

What problem does this feature solve?

When using Microsofts Offic.js (e.g. in Word: https://docs.microsoft.com/en-us/javascript/api/word?view=word-js-preview), and using Vue with vue router, the router tries using push state, but the office js script deletes the push state since the excel web pane is not supporting the push state. The check in push state wheter push state can be used, checks for window.history && 'pushState' in window.history, but office.js resets the fields on window.history, not window.history itself (see https://stackoverflow.com/a/42703406/). I propose we check if Office.js is available in the context, and if so, we disable the push state.

What does the proposed API look like?

We would add a check at this position:

ua.indexOf('Windows Phone') === -1

The additional check could look like this:

const officeJsLoaded = window.Office && window.Office.context;

so the whole function might look like this:

export const supportsPushState =
  inBrowser &&
  (function () {
    const ua = window.navigator.userAgent
    const officeJsLoaded = window.Office && window.Office.context

    if (
      officeJsLoaded ||
      ((ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
      ua.indexOf('Mobile Safari') !== -1 &&
      ua.indexOf('Chrome') === -1 &&
      ua.indexOf('Windows Phone') === -1)
    ) {
      return false
    }

    return window.history && 'pushState' in window.history
  })()

Alternatively, we could check if window.history.pushState is null, but I am not sure of the implications of this. Maybe someone who knows the expected state in the browsers can shed a light wheter that would make sense.

This would result in this return statement, the rest of the function not being changed:

return window.history && 'pushState' in window.history && window.history.pushState !== null
@posva
Copy link
Member

posva commented Mar 23, 2020

I think checking for history.pushState to be a function should be enough

@Mafii
Copy link
Author

Mafii commented Mar 23, 2020

I found a workaround so vue router doesn't have to be changed:

Before importing vue router, add the following lines:

delete window.history.pushState; // workaround to make sure vue router doesn't try using pushState
delete window.history.replaceState; // workaround to make sure vue router doesn't try using replaceState

Then, import vue router and init it, before initializing office.js.
Seems to work for me.

@hengchengfei
Copy link

@Mafii
1

@Mafii
Copy link
Author

Mafii commented Oct 17, 2023

@hengchengfei I have no clue and have not worked on any of this in years, but what about replacing the second line's pushState with replaceState?

Anyways, your error is purely a typescript error, so you could just disable typescript checks on this (using as any?) if the types don't work with it? And, it should be fixed anyways (see the closing commit), so maybe just comment out the lines and see if everything works?

@points-unknown
Copy link

Forgive me for digging up an old thread, but I don't understand how to " import vue router and init it, before initializing office.js."

Here is my main.js code:

import { createApp } from "vue";
import App from "./App.vue";

delete window.history.pushState; // workaround to make sure vue router doesn't try using pushState
delete window.history.replaceState; // workaround to make sure vue router doesn't try using replaceState
import router from "./router";

const app = createApp(App);
app.use(router);
window.Office.onReady(() => {
  app.mount("#app");
});

and I call the office.js script in index.html:

<head>
    <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
</head>

Doing this does not work. If I don't load office.js and I don't use Office.onReady() I can get the page to at least show up. But any mention of office.js or Office.onReady and it loops with errors.

Is there a special order to calling or defining these elements to make the work-around function as intended?

@Mafii
Copy link
Author

Mafii commented Nov 20, 2023

@points-unknown as mentioned before, you should not have to use this workaround! Take a look at the closing commit :)

If that doesn't work, also take a look at https://stackoverflow.com/a/53662709 to help you understand the context. It is possible the way you import officejs in the head is problematic. Sorry I can't help more!

@points-unknown
Copy link

Thank you so much for your help with this. I understand that I shouldn't of had to use the workaround, but vue router wasn't working at all with office.js so I had to do something. Your guidance led me to a solution that worked for me. For those that come here in the future looking for help - here is what I ended up doing that worked:

My router.js file looks like this:

import { createRouter, createMemoryHistory } from "vue-router";

const routes = [
  {
    path: "/",
    name: "home",
    component: Home,
    alias: ["/taskpane", "/taskpane.html"],
  },
     //etc.  go through all of the routes and views
];

const router = createRouter({
  history: createMemoryHistory(),
  routes,
});

export default router;

the key was to change the history to 'createMemoryHistory()' that's what ended up working.

And then for completeness this is what my main.js file looks like:

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";

delete window.history.pushState; // workaround to make sure vue router doesn't try using pushState
delete window.history.replaceState; // workaround to make sure vue router doesn't try using replaceState

const app = createApp(App);

app.use(router);

window.Office.onReady(() => {
  app.mount("#app");
});

I am not sure if I still needed to delete the pushState and replaceState parameters, but it hasn't hurt me yet, so I've left them in.

@abc245
Copy link

abc245 commented Feb 20, 2024

I've spent 8 hours today trying to resolve this, thank you @points-unknown!

@gierschv
Copy link

Thanks @points-unknown! I can reproduce the issue with the latest vue-router on an old MacOS/Safari that the Office team uses to review apps, but doesn't happen with a recent MacOS/Safari.

It crashes around this function call. I tried to reproduce it in a router unit test by setting the 2 functions pushState & replaceState to null, but jsdom crashes an error instead when detecting the feature is not available.
I guess that could be reproduced in a e2e test, but I haven't used nightwatch yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants