0

I'm debugging an Excel add-in built in React. Previously, the developer coded the app as if it was a website rather than an Excel add-in, and did not pay attention to Office.js. As a result, when we run it in Excel, we have the famous error Error: Office.js has not fully loaded. Your app must call "Office.onReady()" as part of it's loading sequence (or set the "Office.initialize" function). If your app has this functionality, try reloading this page.

enter image description here

I searched for Office.onReady and Office.initialize in the whole project, I did not find them.

Here is frontend/src/index.tsx, where dva is a "framework based on redux, redux-saga and react-router (Inspired by elm and choo)".

import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import dva from 'dva';
import './index.css';
import router from './router';
import AuthModel from './models/auth';
import SubscribtionModel from './models/subscription';
import AppModel from './models/app';
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons';

//@ts-ignore
//import createLoading from 'dva-loading';

// 1. Initialize
const app = dva();
//app.use(createLoading());

// 2. Plugins
// app.use({});

// 3. Model
//@ts-ignore
app.model(AuthModel);
app.model(SubscribtionModel)
app.model(AppModel);
// 4. Router
//@ts-ignore
app.router(router);

// 5. Start
app.start('#root');

initializeIcons();

Does anyone know where I should put Office.onReady() such that we could make sure Office.js can fully load every time the add-in is launched?

Edit 1:

I tried to mimic this file and changed one of my index.tsx to

  render() {
    const { items, farItems } = this.props;

    console.log("Here is Office:");
    console.log(Office);

    return (
      <AwaitPromiseThenRender
        promise={Promise.resolve().then(async () => {
              await Office.onReady();
        })}
      >
        <CommonHeader
          items={items.map((item: IHeaderItem) => this.renderItem(item))}
          farItems={farItems.map((item: IHeaderItem) => this.renderItem(item))}
        />
      </AwaitPromiseThenRender>
    );
  } 

It returned a TypeScript error: Property 'onReady' does not exist on type 'typeof Office'. TS2339, whereas the object Office is well printed in the console. Does anyone have any idea of the problem?

Edit 2:

I tried to add the following code in my frontend/src/index.tsx above. It still returned Property 'onReady' does not exist on type 'typeof Office'. TS2339.

export function render(oldRender: () => void) {
    Office.onReady((reason: any) => {
      oldRender();
    });
}
SoftTimur
  • 5,630
  • 38
  • 140
  • 292
  • Are you using Yeoman generator? if you are using Yeoman generator, you can put office.onReady in taskpane.js. here is the document for how to Initialize your Office Add-in https://learn.microsoft.com/en-us/office/dev/add-ins/develop/initialize-add-in this is the link for building an add-in using yoeman generator (react) https://learn.microsoft.com/en-us/office/dev/add-ins/quickstarts/excel-quickstart-react – Raymond Lu Apr 08 '20 at 14:50
  • @RaymondLu I‘m not using Yeoman. I don’t have taskpane.js. The project is big, it’s too late to change how it was generated. – SoftTimur Apr 08 '20 at 14:52
  • in your manifest, you may have SourceLocation section, it states which HTML page your add-in is landing. you could put onReady in this page. – Raymond Lu Apr 08 '20 at 14:56
  • @RaymondLu I may have several buttons/entrypoints in this add-in. Is it possible to put onReady on a higher level, for example in this index.tsx? – SoftTimur Apr 08 '20 at 15:04
  • What do you mean for several entry points to this add-in? does it mean several ribbon buttons? do you want all this entries navitate to the HTML page (which is the SourceLocation in manifest) that taskpane is landing? – Raymond Lu Apr 08 '20 at 15:14
  • @RaymondLu It means several ribbon buttons which lead to different pages. – SoftTimur Apr 08 '20 at 15:15
  • Please see my update... – SoftTimur Apr 08 '20 at 16:12
  • What about putting the `onReady` handler in a lifecycle component like `ComponentDidMount` and once the `onReady` handler fires, it can set something like `this.state.loading` to false. Then in your `render` function, you can render a loader until it is ready? – kennyvh Apr 08 '20 at 16:22

2 Answers2

0

You can put it in componentWillMount

async componentWillMount() {
 await hostApp.onReady();}
Ketobomb
  • 2,108
  • 1
  • 17
  • 27
0

If you use class component,you can put it in componentWillMount.

If you use function component, you can put it in Effect hooks like this:

useEffect(()=>{
    Office.onReady(function(info) {
      console.log(info);
      if (info.host === Office.HostType.Word) {
        console.log("hhhhhhhhhhh")
      }
      if (info.platform === Office.PlatformType.PC) {
        // Make minor layout changes in the task pane.
        console.log("ooooooooooooo")
      }
      console.log(`Office.js is now ready in ${info.host} on ${info.platform}`);
    });
  })
nik7
  • 806
  • 3
  • 12
  • 20