I have two external tables that contain an ID and a description. They are the basis for a set of menus, and since they are very similar in their schema, I'm attempting to create one tool for creating an array of code and description pairs. In addition, I'm using this as a learning tool for defining modules and it's not going so well. These these will eventually be input for menus, as well as look
These are two different tables and the field names are not identical, so I need to account for that as well.
In my Map module, myMap.js, I have defined this variable, which is a table I have loaded as a featureLayer, along with several other layers.
var specUrl = specialtyTable.url;
added a listener for the map load event:
myPopulateCodeList.populateCode('spec',specUrl);
The function populateCode is getting called, but I need to keep the codeList array as a variable, because I'll use it again in more than one place.
myPopulateCodeList.js
define([ "dojo/on", "esri/tasks/QueryTask", "esri/tasks/query" ], function( on,QueryTask, Query){ var codeList = []; return { populateCode: function(codeType, url){ switch (codeType) { case "spec": codeAtt = 'ID_SPECIALTY_PK'; specAtt = 'TX_SPECIALTY_DESC'; break; default: codeAtt = 'ID_PROV_TYPE_PK'; specAtt = 'TX_PROV_DESC'; break; } var queryTask = new QueryTask(url); var query = new Query(); query.outFields = ["*"]; query.where = "1=1"; query.returnGeometry = false; on(queryTask, 'complete', this.codeResultsHandler); on(queryTask, 'error', this.errorHandler); queryTask.execute(query); }, //builds codeList object containing codes and descriptions codeResultsHandler: function(results){ var numResults = results.featureSet.features.length; for (var j = 0; j < numResults; j++) { var code = results.featureSet.features.attributes[codeAtt]; var desc = results.featureSet.features.attributes[specAtt]; codeList.push({ code: code, desc: desc }); } console.log('end of codeList array build'); return codeList; }, errorHandler: function(err){ console.log("error in myPopulateCodeList, error: " + err.details); } } return codeList; });
I can see that codeList is getting populated with values from the results handler of the queryTask, but I can't figure out how to pass this result back to the calling function. This doesn't work:
var specCodeList = myPopulateCodeList.populateCode('spec',specUrl);
When I had it all in one big file, this was working, but I'm trying to learn how to break my work up into smaller bits.
Solved! Go to Solution.
Yes seems like your getting a reference to myPopulateCodeList.codeList. If your going to store those codeLists in the global scope then I would recommend modifying the module a little so codeList is not a property of myPopulateCodeList to prevent issues like this. Here is an example of the original sample I posted with some changes to make codeList not a property of myPopulateCodeList module.
define([ "dojo/on", "esri/tasks/QueryTask", "esri/tasks/query", "dojo/_base/lang", "dojo/Deferred" ], function( on, QueryTask, Query, lang, Deferred){ var mo = { //codeList: [], We don't want this as a property here, it should be a local variable in function below populateCode: function(codeType, url){ var codeAtt, specAtt, codeList = []; // Added local codeList here var deferred = new Deferred(); switch (codeType) { case "spec": codeAtt = "ID_SPECIALTY_PK"; specAtt = "TX_SPECIALTY_DESC"; break; default: codeAtt = "ID_PROV_TYPE_PK"; specAtt = "TX_PROV_DESC"; break; } var queryTask = new QueryTask(url); var query = new Query(); query.outFields = ["*"]; query.where = "1=1"; query.returnGeometry = false; queryTask.execute(query, lang.hitch(this,function(results){ var numResults = results.featureSet.features.length; for (var j = 0; j < numResults; j++) { var code = results.featureSet.features.attributes[codeAtt]; var desc = results.featureSet.features.attributes[specAtt]; // push to the local codeList and not this.codeList codeList.push({ code: code, desc: desc }); } console.log("end of codeList array build"); deferred.resolve(codeList); }), function(err){ console.log("error in myPopulateCodeList, error: " + err.details); deferred.reject(err); }); return deferred; } }; return mo; });
What was happening was that when you called the populateCode function, it was updating the myPopulateCodeList.codeList property. So when you call populateCode function it just updates the same property no matter what you pass into it. Doing it this way returns a new codeList each time and then you can append it to your global codeLists
Is there any reason why you just don't declare codeList as a global variable? Stands to reason this is why it probably worked when everything was in one long JS file.
Either declare it at the top of your myMap.js file (outside of the requires):
var codeList = [];
or if you don't like lots of individual global variables, create a single global object and then create/update codeList as you need to at the module level:
Create global object in myMap.js before requires:
var app = []; app.codeList = [];
Create/update values down at the module level:
app.codeList.push({...
I've been really getting my feet wet lately with migrating my legacy JS over to AMD and, although it's entirely possible that I'm doing this wrong, I'm not returning anything from my created AMD modules:
define ([ "dojo/_base/declare", "esri/tasks/PrintTemplate", "esri/tasks/PrintParameters", "esri/tasks/PrintTask" ], function ( declare, PrintTemplate, PrintParameters, PrintTask ) { return declare(null,{...
I've had some issues when I want to pass a value WITHIN a module from one function to another (i.e. two levels down and trying to use this) but I generally find my module approach and the app.[object] approach works well enough for what I've been trying to do. Again, maybe I'm missing something or being a bit too simplistic.
I'm trying not to have too many global variables, because once I have this code broken up, I know I'll have problems keeping track of where I'm settnig them.
I have two tables I need to process, and both arrays needs to be persistent. I have a field for specialty code and a field for provider type and each has a corresponding lookup table. Since that's the case, I can't just re-use codeList. I need something like specCodeList and providerCodeList.
Technically you don't have to have your code divided into modules to use AMD syntax. I switched from legacy to AMD a couple years ago. When I did it, though, I left it all in one file the same as I had it before. My projects aren't generally very big, so I felt OK about leaving that at that state. The project I have now is working just great, but it is now 1200 lines long and I'm tired of scrolling. Even if I've not fully embraced AMD, if I could get it into manageable chunks, I'd be better off.
Tracy,
This is how I would code your module:
define([ "dojo/on", "esri/tasks/QueryTask", "esri/tasks/query", "dojo/_base/lang" ], function( on, QueryTask, Query, lang){ var mo = { codeList: [], populateCode: function(codeType, url){ var codeAtt, specAtt; switch (codeType) { case "spec": codeAtt = "ID_SPECIALTY_PK"; specAtt = "TX_SPECIALTY_DESC"; break; default: codeAtt = "ID_PROV_TYPE_PK"; specAtt = "TX_PROV_DESC"; break; } var queryTask = new QueryTask(url); var query = new Query(); query.outFields = ["*"]; query.where = "1=1"; query.returnGeometry = false; queryTask.execute(query, lang.hitch(this,function(results){ var numResults = results.features.length; for (var j = 0; j < numResults; j++) { var code = results.fetures.attributes[codeAtt]; var desc = results.features.attributes[specAtt]; this.codeList.push({ code: code, desc: desc }); } console.log("end of codeList array build"); return this.codeList; }), function(err){ console.log("error in myPopulateCodeList, error: " + err.details); }); } }; return mo; });
I would expect the 'mo' that is returned to be the codeList array, but when I call it from myMap.js as
var specCodeList = myPopulateCodeList.populateCode('spec',specUrl);
it is still undefined.
I think it returning undefined looks to be the correct behavior the way that Robert Scheitlin, GISP defined it. Calling that populateCode function does not actually return anything it is just updating the codeList property. So I think you could try something like this:
myPopulateCodeList.populateCode('spec', specUrl);
console.log(myPopulateCodeList.codeList);
logging myPopulateCodeList.codeList should see some things have updated, but remember populateCode is an async function and may take time to complete so the data may not be available right away. Async operations are great to use dojo's deferred library.
Here is an example of Robert Scheitlin, GISP code but with deferred in it
define([ "dojo/on", "esri/tasks/QueryTask", "esri/tasks/query", "dojo/_base/lang", "dojo/Deferred" ], function( on, QueryTask, Query, lang, Deferred){ var mo = { codeList: [], populateCode: function(codeType, url){ var codeAtt, specAtt; var deferred = new Deferred(); switch (codeType) { case "spec": codeAtt = "ID_SPECIALTY_PK"; specAtt = "TX_SPECIALTY_DESC"; break; default: codeAtt = "ID_PROV_TYPE_PK"; specAtt = "TX_PROV_DESC"; break; } var queryTask = new QueryTask(url); var query = new Query(); query.outFields = ["*"]; query.where = "1=1"; query.returnGeometry = false; queryTask.execute(query, lang.hitch(this,function(results){ var numResults = results.featureSet.features.length; for (var j = 0; j < numResults; j++) { var code = results.featureSet.features.attributes[codeAtt]; var desc = results.featureSet.features.attributes[specAtt]; this.codeList.push({ code: code, desc: desc }); } console.log("end of codeList array build"); deferred.resolve(this.codeList); }), function(err){ console.log("error in myPopulateCodeList, error: " + err.details); deferred.reject(err); }); return deferred; } }; return mo; });
Then you can use this in your code like so:
myPopulateCodeList.populateCode('spec', specUrl).then(function (codeList) { console.log(codeList); }, function (err) { console.error(err); });
Note this is not tested but should work fine. Anytime you want to make asynchronous requests, dojo/Deferred can be a great utility to have if you know how to use it. There is a lot of information on them here: dojo/Deferred — The Dojo Toolkit - Reference Guide
Maybe I need to also incorporate the idea that Steve brought up. I need these codeList to continue to be available throughout the application. I'm actually using them as a lookup table, so every time I create a grid or info tag, I need them to look up what the description is to go with the code.
In my index.html I have defined
var app = {};
I guess I need to continue to make this available to my other .js files by passing it as a parameter?
If you follow what I was throwing out there, it should be
var app = [];
not
var app = {};
I can definitely see that I need to handle this is in a my async manner. The calling script myMap.js is getting to the console.log ('UR here') way before it finishes the results of the queryTask. I should mention the external table is a DB2 business table, nothing in geodatabase format, and I always operate under the assumption it's going to take longer to process any query.
Listing myPopulateCodeList.codeList in the console log displays an empty array.
I should also mention for anyone who wants to use this code for themselves that the queryTask results must be referenced as results.features, not results.featureSet.features. In my original posting, I had a separate results handler. When you call a separate results handler, that's when you need to results.featureSet.features to get to the individual features.