Hi everyone,
I am pretty new to WAB and Javacript development with Dojo. My plan is to generate a dynamic Combobox out of feature-layer attributes. On click the widget should add that specific feature-layer with the chosen layerexpression. e.g. Country selection ComboBox: "USA" -> Zoom on USA.
I got this to work in a standalone Javascript api, but now i'd like to include this into my WAB1.2 functionality set as a widget and I ran into several Dojo issues which I am not familiar with.
Basically I started a new widget from scratch:
Widget.js
define([ 'dojo/_base/declare', 'jimu/BaseWidget', 'jimu/ConfigManager', "jimu/WidgetManager", 'esri/layers/FeatureLayer', "dijit/form/Select", "dojo/query", "dojo/ready", "dijit/form/ComboBox", "dojo/data/ItemFileReadStore", "dojo/_base/array", "dojo/on" ], function( declare, BaseWidget, ConfigManager, WidgetManager, FeatureLayer, Select, query, ready, ComboBox, ItemFileReadStore, array, on ) { return declare([BaseWidget], { baseClass: 'definition_query', onOpen: function () { function init() { queryTask = new esri.tasks.QueryTask ("https://.../MapServer/0"); query = new esri.tasks.Query(); query.returnGeometry = false; query.outFields = ["NAME"]; query.where = "NAME<> ''"; queryTask.execute(query,populateList); } function populateList(results) { //Populate the ComboBox with unique values var zone; var values = []; var testVals={}; //Add option to display all zoning types to the ComboBox values.push({name:"ALL"}) //Loop through the QueryTask results and populate an array //with the unique values var features = results.features; dojo.forEach (features, function(feature) { zone = feature.attributes.NAME; if (!testVals[zone]) { testVals[zone] = true; values.push({name:zone}); } }); //Create a ItemFileReadStore and use it for the //ComboBox's data source var dataItems = { identifier: 'name', label: 'name', items: values }; var store = new dojo.store.Memory({data:dataItems});//tried to work with ItemFileReadStore before, but couldn't get it to work either console.log(dataItems); console.log(store); dijit.byId("mySelect").set('store', store); } dojo.addOnLoad(init); }, }); });
TypeError: dijit.byId(...) is undefined
dijit.byId("mySelect").set('store', store);
My Issue is dijit.byId won't be interpreted. What kind of modules am I missing? I am unsure how to figure out which of the modules are necessary for this job.
As mentioned, pretty much the same code works on plain javascript api. There are several working examples on JS but I couldn't manage to convert them into WAB. Maybe there is already a widget that has similar functionality?
HTML:
<select id="mySelect" dojoType="dijit.form.ComboBox" style="width:300px;font-size:18px;" value="Select Zoning Type"></select>
Thanks for advice
Solved! Go to Solution.
Ben,
I can't believe that I missed it earlier. But here it is working:
define([ "dojo/_base/declare", "jimu/BaseWidget", "dijit/_WidgetsInTemplateMixin", "dojo/aspect", "jimu/ConfigManager", "jimu/WidgetManager", "esri/layers/FeatureLayer", "dojo/query", "esri/tasks/query", "esri/tasks/QueryTask", "dojo/store/Memory", "dojo/_base/array", "dojo/on", "dojo/_base/lang", "dijit/form/ComboBox" ], function ( declare, BaseWidget, _WidgetsInTemplateMixin, aspect, ConfigManager, WidgetManager, FeatureLayer, dojoQuery, Query, QueryTask, Memory, array, on, lang ) { return declare([BaseWidget, _WidgetsInTemplateMixin], { baseClass: "definition_query", queryTask: null, query: null, postCreate: function () { this.queryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayer..."); this.query = new Query(); this.query.returnGeometry = false; this.query.outFields = ["FDNAME"]; this.query.where = "FDNAME <> ''"; }, onOpen: function () { this.queryTask.execute(this.query, lang.hitch(this, this.populateList)); }, populateList: function (results) { //Populate the ComboBox with unique values var zone; var values = []; var testVals = {}; //Add option to display all zoning types to the ComboBox values.push({ name: "ALL" }); //Loop through the QueryTask results and populate an array //with the unique values var features = results.features; array.forEach(features, function (feature) { zone = feature.attributes.FDNAME; if (!testVals[zone]) { testVals[zone] = true; values.push({ name: zone }); } }); //Create a ItemFileReadStore and use it for the //ComboBox's data source var dataItems = { identifier: "name", label: "name", items: values }; //console.log(dataItems); var store = new Memory({ data: dataItems }); //console.log(store); this.mySelect.set("store", store); } }); });
If "mySelect" is in your widget you would use this.mySelect to reference it.
Thanks Tim for clarification in this case,
even though I changed my code to
dijit.byId(this.mySelect).set('store', store);
dijit.byId still remains undefined and will be thrown
Ben,
You are using a mixture of Legacy and AMD in your code (bad practice).
You need to have 'dijit/_WidgetsInTemplateMixin', and _WidgetsInTemplateMixin in your requires and vars in order to use dojo dijits/widgets in your template html.
Here is your js updated to all AMD and adding necessary requires:
define([ "dojo/_base/declare", "jimu/BaseWidget", "dijit/_WidgetsInTemplateMixin", "jimu/ConfigManager", "jimu/WidgetManager", "esri/layers/FeatureLayer", "dijit/form/Select", "dojo/query", "esri/tasks/query", "esri/tasks/QueryTask", "dijit/form/ComboBox", "dojo/store/Memory", "dojo/_base/array", "dojo/on" ], function ( declare, BaseWidget, _WidgetsInTemplateMixin, ConfigManager, WidgetManager, FeatureLayer, Select, dojoQuery, Query, QueryTask, ready, ComboBox, Memory, array, on ) { return declare([BaseWidget], { baseClass: "definition_query", queryTask: null, query: null, postCreate: function () { this.queryTask = new QueryTask("https://.../MapServer/0"); this.query = new Query(); this.query.returnGeometry = false; this.query.outFields = ["NAME"]; this.query.where = "NAME<> ''"; }, onOpen: function () { this.queryTask.execute(this.query, this.populateList); }, populateList: function (results) { //Populate the ComboBox with unique values var zone; var values = []; var testVals = {}; //Add option to display all zoning types to the ComboBox values.push({ name: "ALL" }); //Loop through the QueryTask results and populate an array //with the unique values var features = results.features; array.forEach(features, function (feature) { zone = feature.attributes.NAME; if (!testVals[zone]) { testVals[zone] = true; values.push({ name: zone }); } }); //Create a ItemFileReadStore and use it for the //ComboBox's data source var dataItems = { identifier: "name", label: "name", items: values }; var store = new Memory({ data: dataItems }); //tried to work with ItemFileReadStore before, but couldn't get it to work either console.log(dataItems); console.log(store); this.mySelect.set("store", store); } }); });
Hi Robert, thanks alot for your work correcting my code! This gives me a huge plus in understanding the differences of Legacy and AMD. Until now it used to be pretty confusing to me and gave me plenty of headaches.
Still I struggle with a few things in this code,
I had to change Line67 from your suggested
array.forEach(features, function (feature) {
to
features.forEach(function (feature) {
on Line 84
var store = new Memory({data: dataItems});
I get "Memory is not a constructor" with dojo.store.Memory it works well (though its bad style), I dont understand why your code does not work - since the dojo/store/memory is properly declared.
and finally on Line 89
this.mySelect.set("store", store);
even if store is properly defined, it throws me off with
detailed error: TypeError: this.mySelect is undefined
Ben,
I should have included the html as well. When working with templated widgets you don't use IDs you use data-dojo-attach-point:
<div> <select data-dojo-attach-point="mySelect" dojotype="dijit/form/ComboBox" style="width:300px;font-size:18px;" value="Select Zoning Type"></select> </div>
Sorry I also see that I left "ready" in the vars and this throws off the require and vars sync. Removing ready should fix the array and memory issue:
define([ "dojo/_base/declare", "jimu/BaseWidget", "dijit/_WidgetsInTemplateMixin", "jimu/ConfigManager", "jimu/WidgetManager", "esri/layers/FeatureLayer", "dijit/form/Select", "dojo/query", "esri/tasks/query", "esri/tasks/QueryTask", "dijit/form/ComboBox", "dojo/store/Memory", "dojo/_base/array", "dojo/on" ], function ( declare, BaseWidget, _WidgetsInTemplateMixin, ConfigManager, WidgetManager, FeatureLayer, Select, dojoQuery, Query, QueryTask, ComboBox, Memory, array, on ) {
Thanks for clarifying to use the data-dojo-attach-point rather then Id. This didn't cross my way so far.
As expected aligning require/vars fixed the memory and array issues.
Now the only problem is getting the store back into the combobox. I still get the
TypeError: this.mySelect is undefined
Ben,
Do you have your widgets html named ExAcTlY "Widget.html" in your widgets folder?
Robert,
yes its properly named like that, I followed the sample naming conventions. The combobox is shown, but without any content.
Ben,
Can you zip-up your whole widget folder and attach it for me to look at?