Programming patterns

Introduction

This section discusses programming patterns and best practices for writing applications with the ArcGIS Maps SDK for JavaScript.

Watching for property changes

reactiveUtils provides the ability to track changes in API properties with a variety of different data types and structures, such as strings, booleans, arrays, collections, and objects. The module also allows for combining properties from multiple sources. It includes the following methods: watch(), on(), once(), when(), and whenOnce().

reactiveUtils also provide TypeScript type checking. You can access properties, build objects or perform other calculations and it is all properly checked by the TypeScript compiler. Callback parameters are also correctly inferred from the getValue function.

Autocasting

Autocasting casts JavaScript objects as ArcGIS Maps SDK for JavaScript class types without the need for these classes to be explicitly imported by the app developer.

In the following code sample, five API classes are needed to create a SimpleRenderer for a FeatureLayer.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require([
  "esri/Color",
  "esri/symbols/SimpleLineSymbol",
  "esri/symbols/SimpleMarkerSymbol",
  "esri/renderers/SimpleRenderer",
  "esri/layers/FeatureLayer",
], (
  Color, SimpleLineSymbol, SimpleMarkerSymbol, SimpleRenderer, FeatureLayer
) => {

  const layer = new FeatureLayer({
    url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/WorldCities/FeatureServer/0",
    renderer: new SimpleRenderer({
      symbol: new SimpleMarkerSymbol({
        style: "diamond",
        color: new Color([255, 128, 45]),
        outline: new SimpleLineSymbol({
          style: "dash-dot",
          color: new Color([0, 0, 0])
        })
      })
    })
  });

});

With autocasting, you don't have to import renderer and symbol classes; the only module you need to import is esri/layers/FeatureLayer.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require([ "esri/layers/FeatureLayer" ], (FeatureLayer) => {

  const layer = new FeatureLayer({
    url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/WorldCities/FeatureServer/0",
    renderer: {                        // autocasts as new SimpleRenderer()
      symbol: {                        // autocasts as new SimpleMarkerSymbol()
        type: "simple-marker",
        style: "diamond",
        color: [ 255, 128, 45 ],       // autocasts as new Color()
        outline: {                     // autocasts as new SimpleLineSymbol()
          style: "dash-dot",
          color: [ 0, 0, 0 ]           // autocasts as new Color()
        }
      }
    }
  });

});

To know whether a class can be autocasted, look at the ArcGIS Maps SDK for JavaScript reference for each class. If a property can be autocasted, the following image will appear:

autocast label

For example, the documentation for the property renderer of the FeatureLayer class has an autocast tag.

Notice the code using autocasting is simpler and is functionally identical to the above code snippet where all the modules are explicitly imported. The ArcGIS Maps SDK for JavaScript will take the values passed to the properties in the constructor and instantiate the typed objects internally.

Keep in mind there is no need to specify the type on properties where the module type is known, or fixed. For example, look at the outline property in the SimpleMarkerSymbol class from the snippet above. It doesn't have a type property because the only Symbol subclass with an outline property is SimpleLineSymbol.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
const diamondSymbol = {
  type: "simple-marker",
  outline: {
    type: "simple-line", // Not needed, as type `simple-line` is implied
    style: "dash-dot",
    color: [ 255, 128, 45 ]
  }
};

In cases where the type is more generic, such as FeatureLayer.renderer, then type must always be specified for autocasting to work properly.

Widget viewModel pattern

Additional functionality can be implemented using the viewModel for many of the widgets. Each widget has a view and a viewModel. The view is generally responsible for handling the User Interface (UI) of the widget, meaning how the widget displays and handles user interaction via the DOM, for example the Sketch widget. The viewModel part is responsible for the underlying functionality of the widget, or rather, its business logic, for example SketchViewModel.

Why divide the widget framework into these two separate parts? One reason is reusability. The viewModel exposes the API properties and methods needed for functionality required to support the view, whereas the view contains the DOM logic. Since viewModels extend from esri/core/Accessor, they take advantage of all Accessor's capabilities. This helps keeps consistency between various parts of the API since many other modules derive from this class as well.

So how do these two parts work together? When a widget renders, it renders its state. This state is derived from both view and viewModel properties. At some point within the widget's lifecycle, the view calls upon the viewModel's methods/properties which causes a change to a property or result. After a change is triggered, the view is then notified and will update on the UI.

Here is an example using the SketchViewModel.polygonSymbol property to override the default drawing symbology while actively creating a new graphic:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const polygonSymbol = {
  type: "simple-fill", // autocasts as new SimpleFillSymbol()
  color: "#F2BC94",
  outline: {
    // autocasts as new SimpleLineSymbol()
    color: "#722620",
    width: 3
  }
};

const sketchViewModel = new SketchViewModel({
  view: view,
  layer: graphicsLayer,
  polygonSymbol: polygonSymbol,
});

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.