JS API Dojo AMD - Need a good way to code methods\fucntions without spaghetti code.

4131
4
08-24-2015 12:18 PM
MikeCrook
New Contributor III

I'm using JS API Dojo AMD's require function (one require function that drive everything). The function is expanding more and more with each control that I add. I don't want this becoming uncontrollable and getting out-of-hand. Any good manageable way to handle something like this? I'm by no means an expert JS developer. Any help is appreciated.

0 Kudos
4 Replies
TimWitt2
MVP Alum

A lot of people like to list them one at a time and divided into categories (dojo, esri...)

require([
"esri/map", 

"esri/layers/ArcGISDynamicMapServiceLayer", /// All the layers
"esri/layers/ImageParameters",

"esri/dijit/BasemapToggle", /// All the dijits
"esri/dijit/HomeButton",

"dojo/on", /// All the Dojo
"dojo/dom",
"dojo/domReady!"],
KenBuja
MVP Esteemed Contributor

I'd start reading some of Rene Rubalcava​ blogs here on GeoNet: GeoDev Adventures​ One recent post talked about beginning to modularize your code: Thinking in modules

There are more posts on his personal blog, in addition.

ChrisSmith7
Frequent Contributor

I second modularizing your code. Also, regarding modules, you can specify multiple package locations to further compartmentalize:

Configuring Dojo with dojoConfig - Archived Tutorial - Dojo Toolkit

var dojoConfig = {
  packages: [
       "package1",
       "package2"
  ],
  paths: {
       package1: "../lib/package1",
       package2: "/js/package2"
  }
};

// ...is equivalent to:
var dojoConfig = {
  packages: [
       { name: "package1", location: "../lib/package1" },
       { name: "package2", location: "/js/package2" }
  ]
};

Oh, and one more thing - regarding adding more requires in your main module, you can group like Tim suggests. I simply keep them sorted alphabetically - you can just throw it in Excel with the module name you gave and sort to keep the ordering in synch.

TracySchloss
Frequent Contributor

I finally took the plunge a couple weeks ago and decided I really needed to break up my code.  I think 'break it into modules' sounds good, but it doesn't really explain how to do it.

First of all, you don't have to have break it up into tiny pieces.  Just breaking the major functionality of my code into separate files has helped me keep track.

So far I am mostly just using my code for functions.

First I create a new folder in your project.  This one is called extras, although it could be anything as long as you keep track of the name.

Then I add this folder to my dojoConfig as an additional package.  I've also been using bootstrap-map, so there's an entry for it as well.

  <script type="text/javascript">
      window.dojoConfig = {
        async: true,
        parseOnLoad:false,
        packages: [
        {
            name: 'bootstrap-map-js',
            location: '//esri.github.io/bootstrap-map-js/src/'
          },{
            name: 'extras',
            location: window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/extras"
          }
        ]
      };
    </script>

Then in my extras folder, I create my .js files

You only need to define the components you're using for this js file as opposed to 'spaghetti' mode where you have a huge list of all modules you need in your code.  This  particular.js file does one thing, create your map. 

There are a few syntax difference between having your functions in index.html and their own .js file.

1) Instead of 'require' now that it's in your .js file, you'll using 'define' instead.

After the function, you'll see

return {

     createMap: function (mapDiv,pathName)

2) This is another syntax difference, the word 'function' doesn't come first, the name of the function does, followed by a colon, then the word 'function' and whatever arguments you will be passing.

define ([  
"dojo/dom-construct",
"dojo/on",
"dojo/dom",

"esri/dijit/Popup",
"esri/SpatialReference", 
"esri/geometry/Extent", 
"esri/layers/FeatureLayer", 
"esri/layers/LabelLayer", 
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",  
"esri/symbols/TextSymbol",  
"esri/Color",
"esri/renderers/SimpleRenderer", 

'bootstrap-map-js/js/bootstrapmap'

 ], function (  

      domConstruct,on,dom,Popup,SpatialReference, Extent, 
      FeatureLayer, LabelLayer,
      SimpleMarkerSymbol, SimpleLineSymbol,TextSymbol,
      Color,SimpleRenderer,BootstrapMap) {  
    return {
       createMap: function(mapDiv, pathName){
        config = {
          countyLayerUrl: pathName + '/arcgis/rest/services/BaseMap/county_simple/MapServer/0'
        }
        app.spatialReference = new SpatialReference({
          wkid: 102100
        });
        var startExtent = new Extent(-10583000, 4287025, -9979000, 4980462, app.spatialReference);
        app.currentExtent = startExtent;
        
        var popup = new Popup({
          markerSymbol: new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 22, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 255, 0]), 2), new Color([255, 255, 0, 0.5]))
        }, domConstruct.create("div"));
        var highlightMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 22, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 255, 0]), 2), new Color([255, 255, 0, 0.5]));
        
        var simpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 8, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0]), 2), new Color([255, 0, 0, 0.5]));
        
        var popup = new Popup({
          markerSymbol: highlightMarkerSymbol
        }, domConstruct.create("div"));     
        
        app.map = BootstrapMap.create(mapDiv, {
          basemap: "gray",
          extent: startExtent,
          sliderPosition: 'upper-left',
          autoResize: true,
          scrollWheelZoom: true,
          fitExtent: true,
          infoWindow: popup
        });
        
        var countyLayer = new FeatureLayer(config.countyLayerUrl, {
          id: "countyLayer",
          mode: FeatureLayer.MODE_ONDEMAND,
          outFields: ["COUNTYNAME"]
        });
        
        //label the counties feature layer      
        var countyColor = new Color("#666");
        var countyText = new TextSymbol();
        countyText.setColor(countyColor);
        countyText.font.setSize("7pt");
        var countyLabelRenderer = new SimpleRenderer(countyText);
        countyLabelLayer = new LabelLayer({
          id: "countyLabels",
          maxScale: 1155582
        });
        
        countyLabelLayer.addFeatureLayer(countyLayer, countyLabelRenderer, '{COUNTYNAME}');      
        app.map.addLayers([ countyLayer, countyLabelLayer]);  
      }
    }

});  

In my index.html, I've been creating a variable window.app = {}.  This is a handy container for defining objects that continue to be available to other modules.  Since I know I will need map, spatialReference etc in other parts of my code, I have defined them in myMap.js as app.map and app.spatialReference, where they'll reside in the app variable to be used again.

Lastly, you need to require myMap, just like you do any others.  Since the createMap function has two arguments, you'll need to pass the pathName and the mapDiv where you want your map to be displayed:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Map - Modules</title>
    <link rel="stylesheet" href="https://community.esri.com//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="https://community.esri.com//js.arcgis.com/3.13/esri/css/esri.css">
    <link rel="stylesheet" type="text/css" href="https://community.esri.com//esri.github.io/bootstrap-map-js/dist/css/bootstrapmap.min.css">
    <link rel="stylesheet" href="https://community.esri.com//xsokev.github.io/Dojo-Bootstrap/css/styles.css">
    <link rel="stylesheet" href="css/app.css">
  </head>
  <body>
    <script type="text/javascript">
      window.dojoConfig = {
        async: true,
        parseOnLoad:false,
        packages: [
         {
            name: 'bootstrap-map-js',
            location: '//esri.github.io/bootstrap-map-js/src/'
          },{
            name: 'extras',
            location: window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/extras" 
          }
        ]
      };
    </script>
      <script type="text/javascript" src="//js.arcgis.com/3.13compact"></script>
  <script type="text/javascript">
    require(["dojo/parser","dojo/on","dojo/dom","extras/myMap","dojo/domReady!"   
    ], 
     function(parser,on,dom,myMap
        ){
      parser.parse();
      
        var pathName = "https://yourservername.gov";
        window.app = {};  //the app variable will be persistent throughout your code
        myMap.createMap(dom.byId('mapDiv'),pathName);   
     });


  </script>
    <div id="mapDiv">
     
    </div> 
    </body>
</html>