9

I am building a custom Power BI visualization, so I have access to a javascript file which is consumed by the platform. I don't have access to any markup, only an element that is injected where I am to mount my visualization.

I am trying to mount a Bing Map, the docs look like this:

    <div id='myMap' style='width: 100vw; height: 100vh;'></div>

    <script type='text/javascript'>
            var map;
            function loadMapScenario() {
                map = new Microsoft.Maps.Map(document.getElementById('myMap'), {});
            }


    </script>

    <script type='text/javascript' src='https://www.bing.com/api/maps/mapcontrol?key=YourBingMapsKey&callback=loadMapScenario' async defer></script>

The URL to the script has a callback querystring param that includes the name of the function to invoke.

Given I don't have access to the markup, I'm trying to do everything dynamically in my visualization's constructor. I create a function, move it to global scope, and then I add the querystring var to reference it, but it never gets invoked. Can you see anything I might be missing?

constructor(options: VisualConstructorOptions) {
        this.host = options.host;
        this.elem = options.element;
        const self = this;

        function moveMethodsIntoGlobalScope(functionName){
            var parts = functionName.toString().split('\n');
            eval.call(window, parts.splice(1, parts.length - 2).join(''));  
        }

        function methodsToPutInGlobalScope(){
            function loadMapScenario(){
                console.log("finally called loadMapScenario");
            }
        }

        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.async = true;

        console.log(loadMapScenario === undefined); // false, definitely in global scope
        script.src = 'https://www.bing.com/api/maps/mapcontrol?key=xxxxxxxxxx&callback=loadMapScenario';
        document.getElementsByTagName('head')[0].appendChild(script);
sandy
  • 283
  • 1
  • 7
  • 27
Mister Epic
  • 16,295
  • 13
  • 76
  • 147

4 Answers4

3

You've defined the functions required to accomplish what you want. However, in the snippet you've given, you're not actually calling upon moveMethodsIntoGlobalScope(), which is why loadMapScenario is undefined. Just add this line before injecting the script.

moveMethodsIntoGlobalScope(methodsToPutInGlobalScope);
jaswrks
  • 1,255
  • 10
  • 13
3

To put a method into the global scope in the browser, you can probably make do with something simpler, like:

window.loadMapScenario = () => {
  console.log("finally called loadMapScenario")
}

I don't think you need moveMethodsIntoGlobalScope or methodsToPutInGlobalScope - you aren't calling either of them from this code, in any case.

qntm
  • 4,147
  • 4
  • 27
  • 41
2

Using foo===undefined behaves differently depending on runtime. Use typeof foo instead when checking if a variable exist.

You are using script.async which will make the script load after all other scripts, you probably want to load it before the other scripts though.

Another trick is to hijack window.onload ...

user5886944
  • 174
  • 1
  • 7
2

I think this code is enough:

window.loadMapScenario = function() {
  console.log("finally called loadMapScenario");
}

const script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = `https://www.bing.com/api/maps/mapcontrol?key=${API_KEY}&callback=loadMapScenario`;
document.getElementsByTagName('head')[0].appendChild(script);

Here is a JSFiddle you can try with your API_KEY: https://jsfiddle.net/9mfzrcfd/2/

vl4d1m1r4
  • 1,688
  • 12
  • 21