Legacy: This topic discusses extending operation views created using the Windows version Operations Dashboard for ArcGIS. This version of the application has been moved to Mature Support status. To learn more about the latest web-based version, see Operations Dashboard for ArcGIS.
The basic steps to creating a configurable widget are as follows:
In this tutorial, you will learn how to enhance the attribute table widget you built in the Create a widget tutorial by adding some configuration logic to it. You'll allow the operation view author to choose the fields of a data source, then use a table to show the chosen attribute information for each feature from the data source.
The same concepts in this tutorial can be applied to configure map tool and feature action extensions.
Note: If you didn't complete the Create a widget tutorial, you can download the source code from here to continue this tutorial.
Before you begin, you must set up a development environment for Windows Operations Dashboard and prepare it to run in developer mode.
After completing the Create a widget tutorial, you should have a folder named tableWidget with the following files:
In the same folder, create the following files:
CheckedMultiSelect
form that will be manifested as
checkboxes for the alpha-numeric fields of the selected data source. The business logic of the template is
implemented in the tableWidgetConfig.js file.
CheckedMultiSelect
form using the fields of the selected data
source, captures the selections from the author, and prepares the selections and other settings to be saved
when the author saves the operation view.
Note: You can assign different names to the above files.
Open the table.json or manifest file in your IDE and add the "path" property to the configuration object to indicate the path to the configuration UI:
"configuration": { "path": " tableConfig.html", "requireDataSource": true },
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="style.css" /> </head> <body> </body> </html>
<body>
tags, create a div
element and add a "data-dojo-type" attribute with
the value "extension/ TableWidgetConfig".
<body> <div data-dojo-type="extension/TableWidgetConfig"></div> </body>
This attribute will be used to instantiate an "extension/TableWidgetConfig" object that has the logic of the table configuration dijit you are going to implement.
Open the TableWidgetConfigTemplate.html file and add the following HTML fragment to define the UI of the table configuration dijit.
In the CheckedMultiSelect <div>
element, set "multiSelectDiv" as the value
for the data-dojo-attach-point property. This "multiSelectDiv" pointer will be used by the TabletWidgetConfig
class to communicate with the configuration UI.
In addition, define the onSelectionChanged event handler when the selection on the form changes (i.e. when a field checkbox is checked or unchecked).
<div class="table"> <div class="table-row"> <label class="configLabel"> Check the fields to show on the attribute table: </label> </div> <div class="table-row full"> <div data-dojo-type="dojox.form.CheckedMultiSelect" data-dojo-attach-point="multiSelectDiv" data-dojo-attach-event="onChange:onSelectionChanged"></div> </div> </div>
<body>
tag, add the following
<script>
block with the dojoConfig object to define the module (i.e. the table
configuration dijit) that will load. Set the "async" and "parseOnLoad" properties to true. To learn more about
dojoConfig, read the Dojo Toolkit documentation.
<script> var dojoConfig = { async: true, paths: { "extension": location.pathname.replace(/\/[^/]+$/, '') } }; </script>
Note: Within the "paths" object, the URI of the extension folder is referenced as
"extension". This is used to construct the relative path to the widget configuration dijit (i.e.
"extension/TableWidgetConfig") in the dojo-data-type attribute of a <div>
element in
this file.
After the <script>
block from the previous step, add another <script>
block referencing the ArcGIS API for JavaScript.
You must load the dojoConfig before loading the ArcGIS API for JavaScript, otherwise the dojoConfig will be ignored.
<script src="//js.arcgis.com/3.26/"></script>
<script>
block, add the Dojo parser:
<script> require([ "dojo/parser", "dojo/domReady!" ], function(parser){ parser.parse(); }); </script>
Open the tableWidgetConfig.js file and add the following define
function to define the table
configuration dijit.
In this function, load the dependent modules and pass in a callback function with the modules as parameters.
define([ "dojo/_base/declare", "dojo/_base/lang", "dojo/store/Memory", "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", "esri/opsdashboard/WidgetConfigurationProxy", "dojo/text!./TableWidgetConfigTemplate.html", "dojox/form/CheckedMultiSelect" ], function (declare, lang, Memory, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, WidgetConfigurationProxy, templateString) { });
Declare a class named "TableWidgetConfig".
The second parameter is an array of super classes that are inherited by the TableWidgetConfig class. The third parameter is an object that will contain the methods to be called throughout the lifecycle of the class.
... "dojo/text!./TableWidgetConfigTemplate.html", "dojox/form/CheckedMultiSelect" ], function (declare, lang, _WidgetBase, _TemplatedMixin, WidgetProxy, Memory, Observable, Query, Grid, templateString) { return declare("TableWidgetConfig", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, WidgetConfigurationProxy], { }); });
Inside the TableWidgetConfig class, load the TableWidgetConfigTemplate.html file into the templateString as an inline string.
return declare("TableWidgetConfig", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, WidgetConfigurationProxy], { templateString: templateString, });
Add a postCreate
function. Through the "multiSelectDiv" pointer set
the "multiple" property of the CheckedMultiSelect
form to true so that they will render as
checkboxes. Then, set the labels of the checkboxes to be displayed by the alias of the fields.
postCreate: function () { this.inherited(arguments); this.multiSelectDiv.set("labelAttr", "alias"); this.multiSelectDiv.set("multiple", true); },
After creating the postCreate
function, add a dataSourceSelectionChanged
callback.
It will be called when the table widget is initialized and when the operation view author selects a different data source.
dataSourceSelectionChanged: function (dataSource, dataSourceConfig) { },
The callback for this event accounts for two parameters: dataSource
and dataSourceConfig
.
The dataSource
parameter represents the data source chosen by the operation view author. The
dataSourceConfig
parameter is an object that stores the fields chosen by the author. If you
implement additional properties in your extension, you can also include them in this object.
When the author saves the view, the properties of the dataSourceConfig
object will save with the operation
view. As a result, when the operation view is opened again, these properties will feed back into the
dataSourceConfig
object.
In the dataSourceSelectionChanged
callback, reference the dataSourceConfig
using
this.dataSourceConfig
, and get the data source's alpha-numeric fields. Then create a memory
store object with the fields.
dataSourceSelectionChanged: function (dataSource, dataSourceConfig) { this.dataSourceConfig = dataSourceConfig; var alphaNumericFields = []; dataSource.fields.forEach(function (field) { switch (field.type) { case "esriFieldTypeString": case "esriFieldTypeSmallInteger": case "esriFieldTypeInteger": case "esriFieldTypeSingle": case "esriFieldTypeDouble": alphaNumericFields.push(field); return; } }); var alphaNumericFieldsStore = new Memory({ idProperty: "name", data: alphaNumericFields }); this.multiSelectDiv.set("store", alphaNumericFieldsStore); },
Finally, in the dataSourceSelectionChanged
callback, add the logic to retrieve the previously selected
fields from the dataSourceConfig.selectedFieldsNames property, and use them to check the field boxes.
This part of the logic was needed when the operation view was previously configured and some fields were chosen from the table configuration UI.
dataSourceSelectionChanged: function (dataSource, dataSourceConfig) { ..... // Set previous fields saved in config if (Array.isArray(dataSourceConfig.selectedFieldsNames)) this.multiSelectDiv.set("value", dataSourceConfig.selectedFieldsNames); },
After creating the dataSourceSelectionChanged
callback, implement an onSelectionChanged
callback.
This callback will be called when a checkbox is checked or unchecked. In this callback, the selected fields will be retrieved through the "value" variable. The alias of these fields will then be saved into the selectedFieldsNames property of this.dataSourceConfig.
At the end of the callback, add the logic to check if there is at least one selected field. If there is,
enable the OK button by sending a true flag to the readyToPersistConfig
method
(readyToPersistConfig
is a method inherited from the WidgetConfigurationProxy
's base class).
onSelectionChanged: function (value) { if (!this.dataSourceConfig) return; this.dataSourceConfig.selectedFieldsNames = value; this.readyToPersistConfig(Array.isArray(value) && value.length > 0); }
Your code for the TableWidgetConfig.js file should look like this:
define([ "dojo/_base/declare", "dojo/_base/lang", "dojo/store/Memory", "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", "esri/opsdashboard/WidgetConfigurationProxy", "dojo/text!./TableWidgetConfigTemplate.html", "dojox/form/CheckedMultiSelect" ], function (declare, lang, _WidgetBase, _TemplatedMixin, WidgetProxy, Memory, Observable, Query, Grid, templateString) { return declare("TableWidgetConfig", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, WidgetConfigurationProxy], { templateString: templateString, postCreate: function () { this.inherited(arguments); this.multiSelectDiv.set("labelAttr", "alias"); this.multiSelectDiv.set("multiple", true); }, dataSourceSelectionChanged: function (dataSource, dataSourceConfig) { this.dataSourceConfig = dataSourceConfig; var alphaNumericFields = []; dataSource.fields.forEach(function (field) { switch (field.type) { case "esriFieldTypeString": case "esriFieldTypeSmallInteger": case "esriFieldTypeInteger": case "esriFieldTypeSingle": case "esriFieldTypeDouble": alphaNumericFields.push(field); return; } }); var alphaNumericFieldsStore = new Memory({ idProperty: "name", data: alphaNumericFields }); this.multiSelectDiv.set("store", alphaNumericFieldsStore); // Set previous fields saved in config if (Array.isArray(dataSourceConfig.selectedFieldsNames)) this.multiSelectDiv.set("value", dataSourceConfig.selectedFieldsNames); }, onSelectionChanged: function (value) { if (!this.dataSourceConfig) return; this.dataSourceConfig.selectedFieldsNames = value; this.readyToPersistConfig(Array.isArray(value) && value.length > 0); } }); });
Open the tableWidget.js file. Inside the hostReady
method, using the selected fields from the
dataSourceConfig
object, replace the following code block:
replace
dataSource.fields.forEach(function (field) { switch (field.type) { case "esriFieldTypeString": case "esriFieldTypeSmallInteger": case "esriFieldTypeInteger": case "esriFieldTypeSingle": case "esriFieldTypeDouble": fieldsToQuery.push(field.name); columns.push({field: field.alias}); return; } });
with the code block below:
dataSourceConfig.selectedFieldsNames.forEach(function (field) { columns.push({field: field}); });
Similarly, in the same method update the fieldsToQuery
array so that it will query for the fields from the
selectedFieldsNames
property saved to the dataSourceConfig
object. Replace the following code block:
replace
fieldsToQuery.push(dataSource.objectIdFieldName);
with the below code block:
var fieldsToQuery = dataSourceConfig.selectedFieldsNames.slice(); if (fieldsToQuery.indexOf(dataSource.objectIdFieldName) === -1) fieldsToQuery.push(dataSource.objectIdFieldName);
Now, the names of the fields chosen by the operation view author through the table widget's Settings dialog
will be extracted from the dataSourceConfig
object. These fields will then be queried by the chosen data source
and the results will be rendered in the attribute table.
Open the style.css file. At the top of the file, import the style for the CheckedMultiSelect
form
the ArcGIS API for JavaScript.
@import "//js.arcgis.com/3.26/dojox/form/resources/CheckedMultiSelect.css";
Update the styles for the configuration UI.
.dataSourceInfoLabel { font-weight: bold; margin-bottom: 10px; } .configLabel { margin-bottom: 10px; } .dojoxCheckedMultiSelect, .dojoxCheckedMultiSelectWrapper { height: inherit !important; margin: 0 !important; overflow-y: auto !important; border: none !important; } .dijitInline.dojoxMultiSelectItemLabel, .dijitInline.dijitCheckBox { display: inline; }
Your extension is now ready for testing in Operations Dashboard.
Note: To learn more about creating an operation view, see Create an operation view in the PDF of the operation view help.
Tip: When an extension is loaded in the developer mode, it will have a red bounding box to help you distinguish it from extensions in production.
The UI under the Data tab originates from the tableConfig.html file. You should see the checkboxes created for the alpha-numeric fields of the currently chosen data source.
Tip: You can also click the Dev Mode text at the upper right corner of the app, then click Open Debug Tool to launch the debugger.
The URL to the HTML page shown on the debugger starts from localhost:<port number> because the extension is running from a local server.
Tip: If you make a change to the HTML or JS file during debugging, you can click the Refresh button at the top left corner of the debugger to get the update. However, if you make any changes to the manifest, you will need to save the operation view, then close and reopen it.
If you want to debug your extension using the web browser, follow the steps below.
The operation view will open in your default browser.
Note: Since you are hosting your extension in the local server, you need to keep the Windows app and the local server running.