Select to view content in your preferred language

EsriJS App Structure Opinions

1043
3
08-26-2015 09:06 AM
Labels (1)
ReneRubalcava
Esri Frequent Contributor
1 3 1,043

esrijs-structure.jpg

Every now and then, someone will ask me about how they should structure their ArcGIS JS API app. In my book, I recommend the following app structure:

app/

   |-- css/

   |-- js/

         |-- controllers/

         |-- services/

         |-- utils/

         |-- widgets/

         |-- main.js

         |-- run.js

   |-- index.html

I've used this app structure for a lot of projects and it's worked fine. Typically my run.js looked like this:

(function() {
  'use strict';
  var pathRX = new RegExp(/\/[^\/]+$/)
  , locationPath = location.pathname.replace(pathRX, '');
  require({
  packages: [{
  name: 'controllers',
  location: locationPath + 'js/controllers'
  }, {
  name: 'widgets',
  location: locationPath + 'js/widgets'
  }, {
  name: 'utils',
  location: locationPath + 'js/utils'
  }, {
  name: 'services',
  location: locationPath + 'js/services'
  }, {
  name: 'app',
  location: locationPath + 'js',
  main: 'main'
  }]
  }, ['app']);
})();

This works out pretty nice. But one thing it doesn't take into consideration is treating the app and all submodules as an app package. This is more concerned with treating my modules as packages when using the CDN. What's the difference? Glad you asked!

Let's say you want to build your application. Maybe you're going to utilize the ArcGIS optimizer or esri-slurp, which has a great example here by the way. You're going to want to treat your application as it's own package. What do I mean by that? Well, let's take a look at how you use the ArcGIS JS API.

require(['esri/map', 'dojo/declare'], function(Map, declare) {/*cool stuff*/});

In this case, esri is a package and dojo is a package.  There are other packages included in the API, such as dgrid, dstore, and diijt. This because when you use the Dojo build system, it knows how to reference the files. So you can naturally bundle your application as it's own package, called app. You can call it Sally if you want, but let's assume app works just fine.

So these days, the way I like to structure my app similar to this.

index.html

dojoConfig.js

app/

   |-- styles/

   |-- models/

   |-- services/

   |-- utils/

   |-- widgets/

   |-- main.js

   |-- app.profile.js

    |-- package.json

    |-- config.json

Here, I have dojoConfig file that does some basic setup. It could look like this:

var dojoConfig = {
  async: true,
  parseOnLoad: true,
  isDebug: true,
  deps: ['app/main']
  }
};

Ok, so this assumes I'm going to use esri-slurp to download the API and use bower to install other dependencies. If I were using this as a CDN, I would add packages property like this:

packages: [{
  name: 'app',
  location: location.pathname.replace(/\/[^\/]+$/, '') + 'app'
}]

That's it.

Ok, bear with me a second. What is this app.profile.js nonsense? This file defines some resourceTags for our app package. This basically tells the Dojo build system my package is using AMD. A coworker told me about this, I didn't think I needed it, but the Dojo build system nags you if you don't have it. It looks like this:

var profile = (function(){
  return {
    resourceTags: {
      amd: function(filename, mid) {
        return /\.js$/.test(filename);
      }
    }
  };
})();

The pacakge.json let's the Dojo build system know where to find the app.profile to use.

{
  "name": "myapp",
  "version": "1.0.0",
  "main": "main",
  "description": "Demo app.",
  "homepage": "",
  "dojoBuild": "app.profile.js"
}

The config.json is something that i've been using for years in my ArcGIS JS API apps. It's basically settings or what the map looks like or configurations for widgets. I can use this file or call a web service to get this config data. You can see an example of what that might look like here. The rest of the application is pretty basic. Currently my application structure is heavily inspired by ember-cli as it's something I've been using a lot lately.

However, if I'm using React for building my UI, I like to use a more Flux oriented structure.

index.html

dojoConfig.js

app/

   |-- styles/

   |-- stores/

   |-- actions/

   |-- helpers/

   |-- views/

   |-- main.js

   |-- app.profile.js

   |-- package.json

   |-- config.json

I would also do the same if I were using Angular, where I'd adopt a structure that uses directives instead of views or components. I'm currently working on a Yeoman generator for ArcGIS JS Apps, which you can see a demo app here. It's still pretty experimental, but could be useful to some.

The cmv-app has an interesting app structure using configuration base similar to this starterkit I was working on.

They key here, whether you like my advice or not, is pick a structure that works for you. You could have your main.js work as the application controller and just have a widget folder with all your UI stuff. Again, as long as it works for you, you're all set.

I'll get to a follow-up blog post that talks about the next step here, which is creating a custom build of your application. That should be fun!

For more geodev tips and tricks, check out my blog.

3 Comments
RobertWinterbottom
Deactivated User

Great article, We have been doing a lot of Flux and React lately and are using tons of open source build tools and the last thing for us to integrate is downloading the API from either the Optimizer or esri-slurp and plugging it into our build system. I think the examples here may be a good step in the right direction for us.

Cant wait to see the follow up on creating a custom build with Dojos build system, I am very curious to see how that all works since thats something I have never dug deep into.  We are currently using RequireJS optimizer for our builds just because of the simplicity of it.

ReneRubalcava
Esri Frequent Contributor

r.js will get you about 75%+ of the way there depending on what you are using in your app. Main issue with r.js is that it attempts to execute loader plugins during compilation and will fail on most instances that try to access the DOM, since it's run in Node. Dojo gets around this by using plugin helpers during the build process that r.js does not have. So you can get close to a single-file build, but there are still some modules that will need to be lazy-loaded.

Then there is i18n, which will always be lazy-loaded unless you include all the locales you would need into your build.

The requirejs loader is also missing some methods that Dojo has, mostly for cross-domain loading.

RobertWinterbottom
Deactivated User

Yea the executing of the plugins was a gotcha at first for me.  It use to throw module not found errors because it could not resolve the dojo plugins.  I had some success if the loader plugin was a local module but not if I used the dojo loader plugins.

However I don't use plugins in our apps anymore or any of the other extras.  I use react to render my views have been playing with different methods for css.  Right now I am injecting critical css into the head and then loading our single javascript bundle, then once that is loaded I lazy load the remaining css.  This worked and was able to boost my page speed insights score a bit but did require a slightly different setup.

We originally chose r.js at first because it was simple and we did not need any extras, but I would love to see a writeup on dojos to see what else it can offer and if there is anything that can simplify our current process.

About the Author
Softwhere Developer at Esri working on cool stuff! Author: Introducing ArcGIS API 4 for JavaScript: Turn Awesome Maps into Awesome Apps https://amzn.to/2qwihrV ArcGIS Web Development - https://amzn.to/2EIxTOp Born and raised in East L.A.