1

I have SharePoint 2019, and I am trying to create a webpart that will display Bing Map, I tried this on a react app created using create-react-app, and it works fine. Then I took the code, and added it to SPFx, I am getting no errors, but the div that's supposed to render the map is empty.

Here's my code for the root component:

 return (
      <div>
        <BingMap />
      </div>
    );

Then I have a utility function that'll load scripts when called:

const loadDynamicScript = (config, callback) => {
    const existingScript = document.getElementById(config.id);

    if (!existingScript) {
      const script = document.createElement('script');
      script.src = config.link;
      script.id = config.id;
      document.body.appendChild(script);

      script.onload = () => {
        if (callback) callback();
      };
    }

    if (existingScript && callback) callback();
  };

  export default loadDynamicScript;

Then I have my BingMap component:

import * as React from 'react';
import scriptLoader from '../../utils/scriptLoader'

const config = {
    id: 'bingmaps',
    link: 'https://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=[mykey]'
}
declare global {
    interface Window { Microsoft: any; GetMap: any }
}

window.Microsoft = window.Microsoft || {};
window.GetMap = window.GetMap || {}; 


export default class BingMap extends React.Component<{}, {}> {
     myRef: any;

     GetMap = () => {
      new window.Microsoft.Maps.Map(this.myRef, { });


    componentDidMount() {
        console.log(this.myRef)
        window.GetMap = this.GetMap;
        scriptLoader(config, null)
    }

    render() {
        return (
            <div ref={(myDiv) => { this.myRef = myDiv; }} className="map" ></div>
        )
    }
}

So when my component is mounted, it'll call the script loader, and load bing maps, and execute my callback function. This works in create-react-app, but in SPFx it's not, and I a not getting any errors in the console.

It generates this div:

enter image description here

Any suggestion is appreciated.

0

Dan,

I think the issue may be due to the fact that you're directly modifying the DOM by inserting the script.

For the libraries to be available to the web part, they need to be bundled into the web part either statically or dynamically.

Take a look at this article explaining Dynamic loading of packages in SharePoint Framework. I believe it may help you with your issues.

I hope this helps!

  • Thanks Hugo, I tried that, but I couldn't get it to work, when I set externals, it requires module name, which I didn't know how to set up, I couldn't even find examples to do that with spfx, all about google maps. I am not sure why I can't do it with spfx if I could do it with react? I tried setting this.ref.innerHTML in the call back to be any text, and it worked, so in fact it loaded bing maps, and executed the callback, it just doesn't generate the code for the map. – Dan Mar 24 at 16:58
  • Sorry that you're having issues. If you need an example of embedding a Bing map inside a web part, you should take a look at the following: Dynamic Data Web Part Example. – Hugo Bernier Mar 25 at 17:16
0

Dan, You will have to include the Bing Maps as an external script in webpart as described here- https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/basics/add-an-external-library

In webpart's config.json (inside config folder) include something similar to this:

"externals": {
    "bingmaps": {
      "path": "https://www.bing.com/api/maps/mapcontrol?callback=MyFunction",
      "globalName": "Microsoft"
    }
  },

Install the bingmaps package from npm(this installs the typescript type definitions only, no api code):

npm install --save-dev bingmaps

Then, in your react component import the bingmaps package and use the BingMaps SDK as you were using:

import * as React from 'react';
import styles from './MapsPoc.module.scss';
import { IMapsPocProps } from './IMapsPocProps';
import 'bingmaps';

declare global {
  interface Window { MyFunction: any; }
}

export default class MapsPoc extends React.Component<IMapsPocProps, {}> {

  private mapDivRef: React.RefObject<HTMLDivElement>;
  private mapsInstance: Microsoft.Maps.Map;

  constructor(props){
    super(props);
    this.mapDivRef = React.createRef();

    window.MyFunction = () => {
      if(Microsoft.Maps && this.mapDivRef.current) {
        this.mapsInstance = new Microsoft.Maps.Map(this.mapDivRef.current, {
          credentials: "[bing_maps_api_key]",
          zoom: 5
        });
      }
    }
  }

  public render(): React.ReactElement<IMapsPocProps> {
    return (
      <div 
        className={ styles.MapsPoc }
      >
        <div
          ref={this.mapDivRef} 
          className={ styles.container }
        >
          Loading Maps...
        </div>
      </div>
    );
  }
}

I hope this helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.