Working with a GIS results sometimes in quite advanced workflows, models and so on. The next step might be: How to get these tools shared so other parties and people can work with them? There are multiple approaches to do so:
- publish Geoprocessing as a Web Tool.
- ArcGIS JavaScript API
- Widgets for the Web AppBuilder
In this blog post I would like to describe the last approach and create a customizable Widget for the Web AppBuilder.
Web AppBuilder Dev Edition
You might ask yourself, why do I need the Web AppBuilder Dev Edition (short: WAB)? As we will create a widget is it always good to test the solution prior publishing it as a custom application on AGOL. With the Web AppBuilder Dev Edition we can use template widgets and test the widgets local on a server (or a resource that has a pretty domain...)
WAB runs on Node.js but is only available for Windows at the moment.
Once it is unzipped and configured with your ArcGIS account (connect with Portal for ArcGIS, create an App on your Portal, register the App with your Portal) you're ready to go:


Follow the steps defined at the Get started section.
The Widgets Purpose
For this introduction I would like to create a widget that shows the scale as well as the current zoom level of a map. Of course it is somewhat an enhancement of the standard Scalebar Widget. Therefore I will first show the standalone way and later on describe the enhancement of the default Scalebar widget to do the same: Show not only the Scalebar but also show the zoom level as well as the scale of the underlying map.
A Widget's Structure
A Widget itself consists of a several files that are described below:
- The JavaScript file that defines the widget function (Widget.js)
- The template file that defines the widget’s user interface(Widget.html)
- The widget’s configuration file (config.json)
- The widget's manifest file which is needed for publishing(manifest.json)
- The widget’s i18n strings file for internationalization of strings if needed (nls/strings.js)
- The widget’s style file if you're a fancy guy and like border-rounding, background-images, sliders and circles (css/style.css)
In the end the structure of your widget should look something like this:

As you may have noticed the structure also contains a folder called setting. As we want to customize the behavior of our widget we need to define some settings and interact with them.
The Code
Let's get through the files. We will let the user define, whether or not he/she would like to see the scale, the zoom level or both. This is done in the setting.html
The Setting.html / Setting.js
Therefore we will show two Checkboxes. As I like it plain Vanilla (and didn't got the dijit checkboxes to work 😉 ) we use HTML input checkboxes:
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">class</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>title<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>Zoom Level Info<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">class</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>row<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>input</SPAN> <SPAN class="attr-name token">type</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>checkbox<SPAN class="punctuation token">"</SPAN></SPAN> <SPAN class="attr-name token">id</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>ZoomSetCheckbox<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>input</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>p</SPAN><SPAN class="style-attr language-css token"><SPAN class="attr-name token"> <SPAN class="attr-name token">style</SPAN></SPAN><SPAN class="punctuation token">="</SPAN><SPAN class="attr-value token"><SPAN class="property token">display</SPAN><SPAN class="punctuation token">:</SPAN>inline</SPAN><SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>Show Zoom Info<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>p</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">class</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>row<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>input</SPAN> <SPAN class="attr-name token">type</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>checkbox<SPAN class="punctuation token">"</SPAN></SPAN> <SPAN class="attr-name token">id</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>ScaleSetCheckbox<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>input</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>p</SPAN><SPAN class="style-attr language-css token"><SPAN class="attr-name token"> <SPAN class="attr-name token">style</SPAN></SPAN><SPAN class="punctuation token">="</SPAN><SPAN class="attr-value token"><SPAN class="property token">display</SPAN><SPAN class="punctuation token">:</SPAN>inline</SPAN><SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>Show Approx. Scale<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>p</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
But as this is a widget and we would like to store the default settings in a configuration file called config.json we need to connect the setting.html with the configuration file using the settings.js file.
Therefore we will not only use ids of the buttons (each widget could have items with identical ids...) but also data connectors:
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">class</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>title<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>Zoom Level Info<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">class</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>row<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>input</SPAN> <SPAN class="attr-name token">type</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>checkbox<SPAN class="punctuation token">"</SPAN></SPAN> <SPAN class="attr-name token">id</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>ZoomSetCheckbox<SPAN class="punctuation token">"</SPAN></SPAN> <SPAN class="attr-name token">data-dojo-attach-point</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>ZoomSetCheckbox<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>input</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>p</SPAN><SPAN class="style-attr language-css token"><SPAN class="attr-name token"> <SPAN class="attr-name token">style</SPAN></SPAN><SPAN class="punctuation token">="</SPAN><SPAN class="attr-value token"><SPAN class="property token">display</SPAN><SPAN class="punctuation token">:</SPAN>inline</SPAN><SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>Show Zoom Info<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>p</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">class</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>row<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>input</SPAN> <SPAN class="attr-name token">type</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>checkbox<SPAN class="punctuation token">"</SPAN></SPAN> <SPAN class="attr-name token">id</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>ScaleSetCheckbox<SPAN class="punctuation token">"</SPAN></SPAN> <SPAN class="attr-name token">data-dojo-attach-point</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>ScaleSetCheckbox<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>input</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>p</SPAN><SPAN class="style-attr language-css token"><SPAN class="attr-name token"> <SPAN class="attr-name token">style</SPAN></SPAN><SPAN class="punctuation token">="</SPAN><SPAN class="attr-value token"><SPAN class="property token">display</SPAN><SPAN class="punctuation token">:</SPAN>inline</SPAN><SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>Show Approx. Scale<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>p</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
Now we can communicate with the sates of the buttons.
The setting.js file has two main functions: reading and writing the settings to the config.json file so the widget uses always the correct settings in a new app.
The settings in the config.json file are to set as follow:
<SPAN class="punctuation token">{</SPAN>
<SPAN class="string token">"settings"</SPAN><SPAN class="punctuation token">:</SPAN> <SPAN class="punctuation token">{</SPAN>
<SPAN class="string token">"ZoomSetCheckbox"</SPAN><SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">true</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="string token">"ScaleSetCheckbox"</SPAN><SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">true</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>As visible, the default state of the widget isto enable both checkboxes. The states of the config can be set and read using two functions: getConfig and setConfig (download the zip at the end of the post to get the whole script):
<SPAN class="token function">define</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">[</SPAN><SPAN class="string token">'dojo/_base/declare'</SPAN><SPAN class="punctuation token">,</SPAN><SPAN class="string token">'jimu/BaseWidgetSetting'</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="keyword token">function</SPAN><SPAN class="punctuation token">(</SPAN>declare<SPAN class="punctuation token">,</SPAN> BaseWidgetSetting<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">return</SPAN> <SPAN class="token function">declare</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">[</SPAN>BaseWidgetSetting<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">{</SPAN>
startup<SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">function</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">inherited</SPAN><SPAN class="punctuation token">(</SPAN>arguments<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">log</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>config<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">setConfig</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>config<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">,</SPAN>
setConfig<SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">function</SPAN><SPAN class="punctuation token">(</SPAN>config<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>config <SPAN class="operator token">=</SPAN> config<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>ZoomSetCheckbox<SPAN class="punctuation token">.</SPAN>checked <SPAN class="operator token">=</SPAN> config<SPAN class="punctuation token">.</SPAN>settings<SPAN class="punctuation token">.</SPAN>ZoomSetCheckbox<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>ScaleSetCheckbox<SPAN class="punctuation token">.</SPAN>checked <SPAN class="operator token">=</SPAN> config<SPAN class="punctuation token">.</SPAN>settings<SPAN class="punctuation token">.</SPAN>ScaleSetCheckbox<SPAN class="punctuation token">;</SPAN>
console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">log</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"settings read from file: "</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">log</SPAN><SPAN class="punctuation token">(</SPAN>config<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">,</SPAN>
getConfig<SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">function</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">{</SPAN>
settings <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>config<SPAN class="punctuation token">.</SPAN>settings<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>ZoomSetCheckbox<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
settings<SPAN class="punctuation token">.</SPAN>ZoomSetCheckbox <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>ZoomSetCheckbox<SPAN class="punctuation token">.</SPAN>checked<SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>ScaleSetCheckbox<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
settings<SPAN class="punctuation token">.</SPAN>ScaleSetCheckbox <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>ScaleSetCheckbox<SPAN class="punctuation token">.</SPAN>checked<SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>config<SPAN class="punctuation token">.</SPAN>settings <SPAN class="operator token">=</SPAN> settings<SPAN class="punctuation token">;</SPAN>
console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">log</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="string token">"settings write to file: "</SPAN> <SPAN class="operator token">+</SPAN> settings<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
console<SPAN class="punctuation token">.</SPAN><SPAN class="token function">log</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>config<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">return</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>config<SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>This reads the states of the button (this.XXXSetCheckbox) and writes it in the config for each app it is used in and reads the configuration according the used settings.
The Widget.html / Widget.js
As we covered the settings in the paragraph above let's concentrate on the main code in the Widget.html and Widget.js.
We are only showing two lines in the widget: One line for the Zoom Level, one for the Scale. Therefore the Widget.html file is very short and consists of 2 lines:
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">class</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>ZoomLeveLInfo<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">data-dojo-attach-point</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>zoomInfoDiv<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>Zoom Level: <SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>p</SPAN><SPAN class="style-attr language-css token"><SPAN class="attr-name token"> <SPAN class="attr-name token">style</SPAN></SPAN><SPAN class="punctuation token">="</SPAN><SPAN class="attr-value token"><SPAN class="property token">display</SPAN><SPAN class="punctuation token">:</SPAN> inline<SPAN class="punctuation token">;</SPAN></SPAN><SPAN class="punctuation token">"</SPAN></SPAN> <SPAN class="attr-name token">data-dojo-attach-point</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>MapZoomInfo<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>p</SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>div</SPAN> <SPAN class="attr-name token">data-dojo-attach-point</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>scaleInfoDiv<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN>Approx. Scale: <SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"><</SPAN>p</SPAN><SPAN class="style-attr language-css token"><SPAN class="attr-name token"> <SPAN class="attr-name token">style</SPAN></SPAN><SPAN class="punctuation token">="</SPAN><SPAN class="attr-value token"><SPAN class="property token">display</SPAN><SPAN class="punctuation token">:</SPAN> inline<SPAN class="punctuation token">;</SPAN></SPAN><SPAN class="punctuation token">"</SPAN></SPAN> <SPAN class="attr-name token">data-dojo-attach-point</SPAN><SPAN class="attr-value token"><SPAN class="punctuation token">=</SPAN><SPAN class="punctuation token">"</SPAN>scaleInfo<SPAN class="punctuation token">"</SPAN></SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>p</SPAN><SPAN class="punctuation token">></SPAN></SPAN><SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="token tag"><SPAN class="token tag"><SPAN class="punctuation token"></</SPAN>div</SPAN><SPAN class="punctuation token">></SPAN></SPAN>
<SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
You can see a similar approach as in the Setting.html: We use data-dojo-attach-points to access the div element and alter the content of this div. We could do this inline but we stick to the structure and define the JavaScript part in the Widget.js.
<SPAN class="token function">define</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">[</SPAN>
<SPAN class="string token">'dojo/_base/declare'</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="string token">'dojo/_base/lang'</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="string token">'jimu/BaseWidget'</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="string token">'dojo/on'</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="comment token">//as we alter the div when the map is panned "on.extent-change"</SPAN>
<SPAN class="string token">'esri/map'</SPAN> <SPAN class="comment token">//as we interact with the map</SPAN>
<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="keyword token">function</SPAN><SPAN class="punctuation token">(</SPAN>declare<SPAN class="punctuation token">,</SPAN> lang<SPAN class="punctuation token">,</SPAN> BaseWidget<SPAN class="punctuation token">,</SPAN> on<SPAN class="punctuation token">,</SPAN> Map<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">return</SPAN> <SPAN class="token function">declare</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">[</SPAN>BaseWidget<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">{</SPAN>
baseClass<SPAN class="punctuation token">:</SPAN> <SPAN class="string token">'jimu-widget-zoomlevelinfo'</SPAN><SPAN class="punctuation token">,</SPAN>
postCreate<SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">function</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">inherited</SPAN><SPAN class="punctuation token">(</SPAN>arguments<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">own</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="token function">on</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>map<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">'extent-change'</SPAN><SPAN class="punctuation token">,</SPAN> lang<SPAN class="punctuation token">.</SPAN><SPAN class="token function">hitch</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>_zoomInfo<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">_zoomInfo</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">,</SPAN>
startup<SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">function</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">inherited</SPAN><SPAN class="punctuation token">(</SPAN>arguments<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">,</SPAN>
_zoomInfo<SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">function</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">{</SPAN>
scale <SPAN class="operator token">=</SPAN> esri<SPAN class="punctuation token">.</SPAN>geometry<SPAN class="punctuation token">.</SPAN><SPAN class="token function">getScale</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>map<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">var</SPAN> json <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>config<SPAN class="punctuation token">;</SPAN> <SPAN class="comment token">//read the config</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN>json<SPAN class="punctuation token">.</SPAN>settings<SPAN class="punctuation token">.</SPAN>ZoomSetCheckbox<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">{</SPAN> <SPAN class="comment token">//show Zoom Info set to true</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>MapZoomInfo<SPAN class="punctuation token">.</SPAN>innerHTML <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>map<SPAN class="punctuation token">.</SPAN><SPAN class="token function">getZoom</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN> <SPAN class="comment token">//alter the inner HTML with the Zoom Info</SPAN>
<SPAN class="punctuation token">}</SPAN> <SPAN class="keyword token">else</SPAN> <SPAN class="punctuation token">{</SPAN>
dojo<SPAN class="punctuation token">.</SPAN><SPAN class="token function">destroy</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>zoomInfoDiv<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN> <SPAN class="comment token">//get rid of the div so it looks clean if not wanted</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="keyword token">if</SPAN> <SPAN class="punctuation token">(</SPAN>json<SPAN class="punctuation token">.</SPAN>settings<SPAN class="punctuation token">.</SPAN>ScaleSetCheckbox<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">{</SPAN>
<SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>scaleInfo<SPAN class="punctuation token">.</SPAN>innerHTML <SPAN class="operator token">=</SPAN> <SPAN class="string token">"1:"</SPAN> <SPAN class="operator token">+</SPAN> Math<SPAN class="punctuation token">.</SPAN><SPAN class="token function">round</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>map<SPAN class="punctuation token">.</SPAN><SPAN class="token function">getScale</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="operator token">/</SPAN><SPAN class="number token">1000</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="operator token">*</SPAN><SPAN class="number token">1000</SPAN> <SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN> <SPAN class="keyword token">else</SPAN> <SPAN class="punctuation token">{</SPAN>
dojo<SPAN class="punctuation token">.</SPAN><SPAN class="token function">destroy</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">this</SPAN><SPAN class="punctuation token">.</SPAN>scaleInfoDiv<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>The Manifest.json
To implement the new custom app in a web app we need to create a file called manifest.json. This file holds the basic information about the name and main settings:
{
"name": "ZoomLevelInfo",
"platform": "HTML",
"version": "2.8",
"wabVersion": "2.8",
"author": "Esri Deutschland GmbH // Riccardo Klinger",
"description": "This is a widget to show scale and zoom level of the underlying map.",
"copyright": "",
"license": "http://www.apache.org/licenses/LICENSE-2.0",
"properties": {
"inPanel": false, //as it is off-panel
"hasUIFile": true, // we do have a settings ui file
"supportMultiInstance": false´// we just allow one instance of the widget.
}
}<SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>Now we can test the widget.
Testing the Widget
As we created the main widget we should test it in our WAB Developer installation now. Therefore place the whole folder (in our case ZoomLevelInfo) in the client widget folder of the WAB (in my case "C:\WebAppBuilderForArcGIS\client\stemapp\widgets").
For the test, restart the Web AppBuilder and create a new application:

The widget is now part of the Web AppBuilder and can be selected as a widget:

Hosting the Widget
If your tests were successful you can host the widget on a server and implement it in ArcGIS Online or your ArcGIS Enterprise (>10.5) environment as a custom widget. If you own the portal you might also want to add the folder inside the WAB of the portal (in my case "C:\Program Files\ArcGIS\Portal\apps\webappbuilder\stemapp\widgets").
Download the Example
If you want to use the example, use github here. Or you download it directly below.