ArcGIS JavaScript API 3.0, Dojo 1.7 and AMD Modules
Diving in
For a while now, I have been keeping my JavaScript development modular by using Require.js as I have discussed before. Require.js provides AMD module loading for JavaScript projects. If you are not familiar with AMD, take a look here. Dojo 1.7 is AMD compliant and uses require/define methods to load modules. Up until now, the ArcGIS JavaScript API has been using Dojo < 1.7 for its core. I posted before how I used Require.js to load the API into my applications. Recently, the ArcGIS JavaScript API has been updated to 3.0 and is now using Dojo 1.7 for it’s core. I was pretty excited about this, mainly for AMD modules.
As soon as I saw that 3.0 was out, I dived right in, went straight past go, updated the CDN url in one of my apps and crossed my fingers. Of course it didn’t work. So I did what any dev should do. RTFM. That gave me a little insight, but I figured there must be something in my Require.js config I need to set up, so I went to check out the How to use RequireJS with Dojo page. I know I’ve seen samples of Require.js loading Dojo modules, so I set out to hack away at it. The Require.js/Dojo template was a good read, but has since been abandoned in favor of a non-Require.js template.
Trimming it down
After some fiddling around, I found out the issue of using Require.js with Dojo 1.7 via a CDN was that you can’t and you shouldn’t. When you load Dojo via a CDN, the dojo loader will create a global define and require, negating the need to use Require.js in this case. If I was loading Dojo locally, I could continue using Require.js and load individual Dojo modules as I was initially expecting to do. Now that we know that Require.js is no longer required (pun intended), we can remove that from our upgrade path equation.
I broke it
Let’s return to the
ArcGIS JS 3.0 migration page.
It explains how to load custom modules while using the CDN. I tried this, and
hit a snag. jQuery was broken, which broke my Backbone lib too. I use Backbone a
lot. So ok, more digging. I went through this page on
Using Custom Modules with a CDN.
It seemed pretty straightforward, but jQuery was still broken and other modules
were not working. Part of my problem was I was trying to define my package like
this.
packages: [ { name: "module", location: location.pathname.replace(/\\/[^/]+$/, "") + "src/moduleFolder/moduleFile" } ]
Where “moduleFile” is actually a JavaScript file. You don’t need to add the
*.js, the require function knows what to do. The regex is needed when using the
CDN so that the path will point to the server your app sits on, or else it looks
in the CDN directories and you just get lots of “file not found” errors. I
thought I could bypass the regex with ”./src” and so on, don’t bother, do it
this way, don’t be dumb like me. So I went back and read up on
some more documentation.
Turns out, I am dumb. Packages simply defines the path to a folder. If you want
the name to point to a specific file, you need to define the main field.
packages: [ { name: "module", location: location.pathname.replace(/\\/[^/]+$/, "") + "src/moduleFolder", main: "moduleFile" } ]
I fixed it
At this point, I think I’m good to go, but jQuery is still broken and I’m
getting pretty pissed, but I was on a roll, so I plowed right through with
various config options, then I found
this article.
I followed this and it worked. jQuery worked, Backbone worked, everything was
working. Now I had to know why. It turns out this was the magic piece to get
jQuery to work.
// Add this before first require to launch app define.amd.jQuery = true;
There
is even a page explaining about using
jQuery and AMD
loaders.
I have put together a sample project using ArcGIS JavaScript API 3.0, jQuery and
Backbone. You can find the
source on github. I add a lot of comments
to the
config file,
but I use CoffeeScript, so look at those files for comments. To load configs,
like you would inside an HTML page creating a dojoConfig, you call require
passing it an object with your configs defined.
require({ async: true, packages: [{...}] });
Caveats and Tips
So what does all this mean to ArcGIS JavaScript developers. It means you should
write your modules as AMD compliant.
As of right now, a few ESRI modules work as AMD compliant, meaning you can do
this.
define(['esri/dijit/Popup'], function(){ return new esri.dijit.Popup({...}); });
BUT, it doesn’t work everywhere. Not all ESRI modules are AMD compliant, so for
now I would suggest using the standard dojo.require() method.
If you are used to using the
text plugin for Require.js to
load html templates, have no fear, Dojo has you covered with
dojo/text. I just
had to change my modules where I called text! to dojo/text!.
define(['text!templates/home.html'], function(template){ ... }); // Became this define(['dojo/text!templates/home.html'], function(template){ ... });
I tried creating an alias for this in my dojo config, but couldn’t get it to
work. If someone does, I’d love to hear about it. I was able to fix this by
forming my alias as a proper array or arrays in my
config. In
the config, especially if you are using jQuery, async: true is an important
parameter. jQuery won’t work without it. This specifies that Dojo should be
loaded asynchronously. You can also use it in your require/define methods
similar to the order! plugin for Require.js.
// 1 means true, 0 means false require({async:1},['modules/myModule'], function(myModule){ ... });
I have not tried this method yet, but if I have a need, now I know.
Go forth and update
The biggest hurdle for me was getting the config to work as I needed it to work. Now that I have a better understanding of how Dojo defines packages and loads them, I’ll be moving forward with the ArcGIS JavaScript API 3.x updates. Future updates when the ESRI modules become AMD compliant should be less painful. My old Require.js modules will load just fine now. Most people may not need to move on from Require.js, so you should have a clean slate to work from. Hopefully some of these tips help get you on the track to creating modular apps with the new API.