GEOG 863:
Web Application Development for Geospatial Professionals

4.5.3 The JS

PrintPrint

4.5.3 The JS

Learn More

At this point, you should familiarize yourself with the syntax of JavaScript (JS). The w3schools site offers a good hands-on JavaScript tutorial that should do the job. Take a couple of hours to work through the topics listed under the JS Tutorial heading and pay particular attention to the following points:

  • Ending statements with a semicolon is optional. (The Esri samples use them.)
  • Note the difference between placing JS code inside the page's head section and its body section.  See also this discussion of JS code placement.
  • JavaScript is a dynamically typed language; you do not need to specify a data type when declaring a variable.
  • JavaScript variables are case sensitive.
  • Variables can be initialized on the same line that they are declared.
  • Code associated with a conditional statement (if or else statements) or a loop (for or while) must be enclosed in braces.
  • Alert boxes can be used to debug scripts that aren't working properly.
  • JS functions are reusable blocks of code with an optional return value.
  • The backslash (\) can be used to insert special characters in a string (e.g., if you want a string enclosed in double quotes to also contain a double quote).
  • Comments are signaled in JS using two slashes (//).

Now that you've learned a bit about JavaScript, we can examine what’s happening in the main.js file.

AMD

The first thing you should notice is that all of the code is wrapped inside a require() function. This is a result of the fact that Esri’s API is built around a JS toolkit called Dojo. Dojo uses a design pattern called Asynchronous Module Definition (AMD) to load JS resources into memory. By defining their API’s object classes in modules, Esri’s made those classes easier to maintain and re-use. Loading the modules asynchronously (in parallel rather than one at a time) means that apps built on the API will load faster than they would in a non-AMD framework. One of your recurring tasks as an Esri JS developer will be to identify the modules containing the object classes needed by your app.

In the case of the 2D sample, two Esri classes are needed: Map and MapView. These classes are accessed by loading the esri/Map and esri/views/MapView modules, respectively.  The list of modules at the beginning of the require() function is then followed up by an anonymous callback function. The function is anonymous, in that it has no name. It’s a “callback” function because Dojo's module loader will call back to the function when it's done loading the modules, passing module references to the function.  

In order to work with the classes held in the modules being loaded, the callback function contains a list of parameters that corresponds to the list of modules.  In the 2D example, only the esri/Map and esri/views/MapView modules are required, so the only parameters defined in the callback function are Map and MapView.  We’ll see other parameters associated with other modules in more complex examples later. Some notes on the callback function parameters:

  • The module loader will pass the module references as arguments to the callback function in the same order that the modules were listed.
  • The naming of the parameters is up to you, though the accepted practice is to name them the same as their corresponding modules.
  • As with parameters in any JavaScript function, you can go on to use the parameter as desired in the body of the function.

Note: In the 2021-22 time frame, Esri's samples switched to a "fat arrow" syntax for function declarations.  For example:

   require(["esri/Map", "esri/views/MapView"], (Map, MapView) => {  }

This syntax is a bit more concise than the previously used syntax:

   require(["esri/Map", "esri/views/MapView"], function (Map, MapView) {  }

I've attempted to update my own examples to be consistent with Esri's change, but you may still see the old syntax in places.  If so, no need to worry.  They are equivalent syntaxes for declaring a function. You'll also see some of Esri's samples using the old style too.

The DOM

An important web browser technology relied upon by Esri's JS API is the Document Object Model, or DOM.  This is a tree-like representation of a web page that the browser creates whenever it loads that page. The DOM provides the means for JS developers to manipulate a page’s contents and behavior programmatically. In the case of developing an Esri JS app, the DOM is needed to insert a map or scene onto the page. SInce the require() callback function references DOM objects, it is critical that the DOM is loaded before the callback executes. However, we don't have to worry about this because require() has built-in functionality that waits for the DOM to finish loading before executing the callback function.

Note: Code samples in older incarnations of the API documentation included another module called domReady!  This is a Dojo module that is used to ensure that none of the code that follows in the callback function is executed until the DOM has finished loading.  The fact that Esri removed references to domReady! in their samples implies that there's no longer -- or never was -- a need to worry about the callback function code executing before the DOM was done loading.  In any case, I mention it here in case you run into it either in Esri's or this class's samples.  Since the domReady! module -- also referred to as a plugin in Dojo's documentation -- doesn't have a meaningful return value, you'll note that it doesn't have a parameter to go with it in the callback function parameter list.

Learn More

Once again, the w3schools site offers a good JavaScript HTML DOM tutorial. This short tutorial should only take you 30-60 minutes to complete.

Using the API Reference

Earlier in the lesson, we visited the Esri JS API SDK to locate sample code that could be used to create our first apps. Now, let’s have a look at an equally important part of the SDK: the API Reference. The API Reference contains a list of all the API modules down the left side of the page. Each module heading can be expanded to see the classes defined in that module. For example, we’d find the Map class in the esri module and the MapView class in the esri/views module.

Locating the Map class in the API Reference
Figure 4.2 Locating the Map class in the API Reference

On the right side of the API Reference index page is a list of commonly used classes, generally organized from top to bottom in order of importance. We’ll cover most of these classes over the course of the term.

The first line of JS code in our 2D sample creates an object of the Map class (new Map). We can learn about that class by clicking the Map link on the API Reference index page or by entering Map into the Find page box.

Like the other class description pages in the API Reference, the page for the Map class conveys a lot of useful information:

  • how you’d go about referencing the class in your code’s require() declaration
  • an overview of the class (what it’s used for, a code snippet showing its usage, and links to samples and related classes)
  • the class’s constructors (more on this shortly)
  • the class’s properties (characteristics that uniquely define Map objects)
  • the class’s methods (actions that the Map class is programmed to perform)

The Properties section of the page includes an Overview, listing all of the properties in alphabetical order along with their type (e.g., String, Number, or some object class). The property type is important to know as it tells you what you can expect to get back if you read the property (or conversely, what you need to supply if you’re setting the property). After the Overview, you’ll find a section of Details, providing more specifics on the class’s properties. For example, the details on the basemap property provide a list of acceptable values, along with a thumbnail of each.

The Methods section likewise includes both an Overview and Details section. Method parameters (pieces of information the method uses in performing its action) are listed in parentheses after the name in the Details section. If a parameter is optional, it will be listed with a question mark next to it. If a method returns a value when it is called, the method parameters will be followed by an arrow symbol pointing to the return type. Looking at the Map class methods, the add() method has a required parameter (layer), an optional parameter (index, specifying the position in the Map’s layer collection where you want the layer to appear), and no return value. The findLayerById() method has a required layerId parameter and it returns a reference to a Layer object. Note that when a parameter or return value is an object, it will appear in the documentation as a link. This helps you to navigate through the SDK as you write your code.

The part of the class documentation that we glossed over was the Constructor section. This section is intended to provide guidance on how to create a new object of the class. This is done by using the word new followed by the name of the class and a set of parentheses. In some APIs (including version 3.x of Esri’s JS API), it is possible to set just certain properties of the object you’re creating as part of the constructor statement, making it important to consult the Constructor section of the class’s documentation. However, in version 4.x, any of the class’s properties can be set as part of the constructor, which is why you’ll note that the Map class constructor says simply properties? within the parentheses.

Whatever properties you decide to set in the constructor should be expressed as a JS object literal, a term that may seem intimidating, but is actually not too difficult to grasp. Object literals are basically a list of property-value pairs, in which the list is enclosed in curly braces and the property-value pairs are separated by commas. In the case of the 2D sample, the Map was constructed with just a single property-value setting (the basemap), whereas in the 3D sample, it was constructed with two property-value settings (the basemap and the ground).

Note that you need not set all properties as part of the constructor. For example, I could rewrite the 2D sample as follows:

const map = new Map();
map.basemap = "streets";

Or the 3D sample as follows:

const map = new Map({
  basemap: "streets"
});
map.ground = "world-elevation";

One last word on constructors... When creating an object, you’re typically doing so because you need it later in your script. Thus, you will usually store a reference to the object you’re creating in a variable. In the samples, the new Map object was stored in a variable called map. That variable was later used to set the map property of a MapView object. Having multiple entities in a script with the same name can be confusing, especially for beginners, so I like to assign variable names that don’t duplicate the name of a class or property. In this case, I might use myMap or theMap. Keep in mind though that this means updating other statements in which that variable is referenced:

require([
  "esri/Map",
  "esri/views/MapView"
], (Map, MapView) => {
  const myMap = new Map({
    basemap: "streets"
  });
  const myView = new MapView({
    container: "viewDiv",
    map: myMap,
    zoom: 4,
    center: [15, 65]
  });
});

Note:And finally, one more aspect of the Esri samples that has changed over time is in the way references to objects are stored.  In the past, the samples created variables to store references to objects using the var keyword.  Today, you'll see that the samples now typically employ the const keyword in place of var.  As you might have guessed, const is short for constant.  It is called for when you want to store an object (or primitive value, such as a number or string) in memory for use later in your code, with the important caveat that a constant can never be reassigned a new object or value.  That's in contrast to variables, which as the name implies, can be reassigned a new object or value (i.e., they're allowed to vary).  In the short snippet above, for example, the const myMap and const myView statements were previously written as var myMap and var myView.  Using var still works and is not wrong, but if myMap is always going to refer to the same Map object, it's more appropriate to declare it as a constant. 

In thinking about the difference between const and var, it's important to note that while you can't assign a new object to a constant after you've declared it, you are allowed to set properties of the object to new values.  In other words, if you were adding code to the snippet above, you would not be able to do this:

myMap = new Map({...}); //or const myMap = new Map({...});

but you could do this:

myMap.basemap = "terrain" //same object, just altering a property

If you really wanted to create a new Map object and store it in myMap -- recycling that space in memory, if you will -- you could certainly do that.  You'd just need to declare myMap using var rather than const.  Similarly, if you want to store the number 2 in x and later change x to 3, you would need to declare x using var rather than const.

Lastly, you'll see that the course text generally refers to all named spaces in memory as variables, even those declared using const.  Technically, anything declared using const should be referred to as a constant, but the term variable is so commonly used in programming that I hope you will excuse the imprecise language.  Just keep in mind that most of these named spaces are actually constants, not variables.