Google Maps API fails from ES2015 Application

Viewed 210

I am trying to move my web site to use ES2015 including the use of export and import to control access to shared resources. This means that javascript files which formerly were explicitly included using HTML tags are now accessed using import statements. So I am moving from pages which include the following:

    <script src="/jscripts/util.js" type="application/javascript">
    </script>
    <script src="/jscripts/CommonForm.js" type="application/javascript">
    </script>
    <script src="/jscripts/Cookie.js" type="application/javascript">
    </script>
    <script src="/FamilyTree/Person.js" type="application/javascript">
    </script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?v=3.43&callback=initializeMaps&region=$GOOGLECC&key=$GOOGLEKEY" type="application/javascript"></script>

to pages that do the following:

    <script src="/FamilyTree/Person.js" type="module">
    </script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?v=3.43&callback=initializeMaps&region=$GOOGLECC&key=$GOOGLEKEY" type="application/javascript"></script>

where Person.js starts off with:

import HTTP from "../jscripts/js20/http6.js";
import {iframe, actMouseOverHelp, openFrame, openSignon, debug, args,
        getOffsetLeft, getOffsetTop, popupAlert, show,
        showHelp, hideHelp, helpDiv, helpElt, keyDown,
        eltMouseOver, eltMouseOut}
            from "../jscripts/util6.js";
import {capitalize} from "../jscripts/CommonForm6.js";
import Cookie from "../jscripts/Cookie6.js";

The problem is now my initialization of Google Maps is failing because it cannot find my callback function initializeMaps. That function is still there. The only difference is that farther up that file it contains the import statements.

Uncaught (in promise) 
Object { message: "initializeMaps is not a function", name: "InvalidValueError", stack: "ge@https://maps.googleapis.com/maps/api/js?v=3.43&callback=initializeMaps&region=CA&key=......:70:72\n_.he@https://maps.googleapis.com/maps/api/js?v=3.43&callback=initializeMaps&region=CA&key=....:70:182\nNj@https://maps.googleapis.com/maps/api/js?v=3.43&callback=initializeMaps&region=CA&key=.....:146:233\nOj/<@https://maps.googleapis.com/maps/api/js?v=3.43&callback=initializeMaps&region=CA&key=.....:146:120\n" }

How do I rewrite my Google maps code to work in ES2015?

An example of the production version of the code is https://www.jamescobban.net/FamilyTree/Person.php?idir=90439&lang=en. Hold the mouse over any place name to exploit the Google maps functionality.

1 Answers

It's not clear which module your initializeMaps() function is defined in, but that particular Google API requires that it be a global symbol so Google can find it when that script executes. And, it will have to be defined before the Google script executes too.

With ES6 modules, nothing in the script is automatically defined as globally available. So, unlike the older script files, top level variables in your script only have module scope and are not available globally. This is an important change with ES6 modules. You have to either export a symbol to make it available to other scripts or you have to explicitly make it global by assigning it to the window object (in the browser) like this:

window.initializeMaps = function(...) {...}

It may also be that Google has a more modern interface that doesn't require you go define a global symbol like this so you can use standard import and export syntax.

For more info, you can read about ES6 modules on MDN. Here's a useful set of differences from that web page. Most relevant here is the last bullet point.

Other differences between modules and standard scripts

  • You need to pay attention to local testing — if you try to load the HTML file locally (i.e. with a file:// URL), you'll run into CORS errors due to JavaScript module security requirements. You need to do your testing through a server.

  • Also, note that you might get different behavior from sections of script defined inside modules as opposed to in standard scripts. This is because modules use strict mode automatically.

  • There is no need to use the defer attribute (see <script> attributes) when loading a module script; modules are deferred automatically.

  • Modules are only executed once, even if they have been referenced in multiple <script> tags.

  • Last but not least, let's make this clear — module features are imported into the scope of a single script — they aren't available in the global scope. Therefore, you will only be able to access imported features in the script they are imported into, and you won't be able to access them from the JavaScript console, for example. You'll still get syntax errors shown in the DevTools, but you'll not be able to use some of the debugging techniques you might have expected to use.

Related