Search code examples
javascriptasynchronousrequirejsamdarcgis-js-api

Return value from AMD callback


I am working with the ArcGIS javascript api, which is built on require and the asynchronous module defintion. To create a map, you define all your action inside the callback of the AMD require statement:

require([
  "esri/Map", 
  "esri/views/MapView"
], function(Map, MapView){
   
  const map = new Map({ ... })
  const view = new MapView({ ... })
   
})

I would like to be able to initialize this behavior on command from another module, as well as get access to the map and view objects that are defined within the AMD callback. In order to be able to initialize this on command, I can wrap it in a function, and export that function:

export const makeMap = () => {

   require([esri modules], function(Map, MapView){
   
      map = new Map({ ... })
      view = new MapView({ ... })
   
   })

}

I can import makeMap into some other module in my code, and call it. This is working nicely. However, I am trying to figure out how I can then access the map and view objects to be able to manipulate them through the UI. First I tried this:

// mapMap.js
export const makeMap = () => {

   let mapInstance;
   let viewInstance;

   require([esri modules], function(Map, MapView){
   
      map = new Map({ ... })
      view = new MapView({ ... })

      mapInstance = map   
      viewInstance = view
   
   })

   return { mapInstance, viewInstance }

}

// some other module
import { makeMap } from './makeMap'

const { mapInstance, viewInstance } = makeMap()

This obviously does not work - mapInstance and viewInstance are undefined, as the function that defines them inside the AMD callback runs after they are returned from the makeMap() call.

I am not sure how I can get a returned value from the AMD callback. Is this possible? Do I need another paradigm?

One other thing I tried is to pass in a reference-holder object, apply the reference to that object, and then just retrieve them from there when needed. It works, but I feel its not as clean:

// maprefs.js
export const maprefs = {}

// makeMap.js
import { maprefs } from './maprefs'

export const makeMap = (maprefs, mapname) => {

   require([esri modules], function(Map, MapView){
   
      map = new Map({ ... })
      view = new MapView({ ... })

      maprefs[mapname] = { map, view }
   
   })

}

// some module
import { maprefs } from './maprefs'
import { makeMap } from './makeMap'

makeMap(maprefs, "map1")

someButton.addEventListener('click', () => {
  // maprefs.map1 is properly defined as { mapInstance, viewInstance } and I can take action on it
  maprefs.map1.doSomething
})

I took a look at How do I return the response from an asynchronous call?, which is about retuning values from ajax calls, but I'm struggling to relate that to AMD callbacks.

Is it possible to return the value from the AMD callback to be used elsewhere? I am hoping for a const { map, value } = makeMap() type syntax.


Solution

  • In the scenario that you mention, I think that the best solution is to use esri-loader. Is a tiny library built by ESRI for this purpose exactly, that is, load the ArcGIS JS API library modules at runtime.

    ArcGIS Docs - esri-loader

    Github ESRI - esri-loader usage