Select to view content in your preferred language

timing issue -- anyone who is good with deferreds?

4663
21
Jump to solution
11-12-2013 06:05 AM
KaitlynnDavis
Regular Contributor
I'm building an editing application, and I use a custom drop-down menu for one of the fields in my attribute inspector. The drop down is populated by a query, so it's very important that the query function completes before the function that builds the attribute inspector. My problem is timing the query. My map listener map.on("layers-add-result", initEditor); fires initEditor well before my query has had the chance to complete and as a result my map features become disabled. I can fix the problem by pausing the execution of initEditor with an alert box, but obviously this can't be a permanent solution.

I believe a deffered would help ensure that my query task has returned before populating my attribute inspector, but I am weak on the concept. Is there a programmatic way to say map.on("layers-add-result", initEditor) after query returns results?
0 Kudos
21 Replies
ionarawilson1
Deactivated User
Hi Kaitlynn,

I am having a similar issue, do you think you can share your code? Thank you so much!!!

The big picture is that I want an attribute inspector field that draws from a feature layer called "reports", so people can easily associate the map feature they're adding with the report that it comes from. We'll be continuously adding to the reports dataset, so I can't have a domain with finite values. I couldn't find a way to create a domain in ArcMap that is formulated from a query, just domains where you have to hard code your possible options. So that's why I ended up creating a custom select dijit in my JavaScript.

I was able to resolve my issue with the deferred pattern. As was suggested earlier I want to control when my map layers are getting added to the map, so that the completion of my queries are prioritized before firing the editor init function. So in my init function for the map I created a new Deferred and the instruction to wait until it's resolved to add my map layers

deferred = new dojo.Deferred();

deferred.then(function(value){
        console.log(value);
 map.addLayers([needPoints, needRoutes, needAreas]);
});


Then, at the end of my query I resolved my deferred:

deferred.resolve("success");
return deferred.promise;


This was a tricky one and I very much appreciate the help you gave!
0 Kudos
JianHuang
Deactivated User
If you need to wait for both queryComplete and mapOnload finish to call initEditor(), deferredList is what you are looking for.
http://livedocs.dojotoolkit.org/dojo/DeferredList
0 Kudos
ionarawilson1
Deactivated User
Thank Jian, I tried to use it, but the second time I change something on the combobox, nothing happens because the deferred function already has been resolved.  I have a few feature layers and want just one to be in the template picker depending on the selection in the combobox.

var stewardship = new esri.layers.FeatureLayer("http://tfsgis-iisd01:6080/arcgis/rest/services/FeatureServiceStewAAreaAPoint/FeatureServer/0",
  
  {
          mode: FeatureLayer.MODE_SELECTION, id: 'stewardship',
          outFields: ['*']
   
 });
  var activityarea = new esri.layers.FeatureLayer("http://tfsgis-iisd01:6080/arcgis/rest/services/FeatureServiceStewAAreaAPoint/FeatureServer/1",
  
  {
          mode: FeatureLayer.MODE_SELECTION, id: 'activityarea',
          outFields: ['*']
   
 }); 
          deferred = new dojo.Deferred();

         deferred.then(function(value){
        console.log(value);
        activityValue = dijit.byId('Activity').get('displayedValue')
     if (activityValue == "Forest Stewardship Plan")
  {   map.removeAllLayers;
   alert("TEST")
   map.addLayers([stewardship]);
  }
    if (activityValue == "Non Forest Stewardship Plan")
  {  
   map.removeAllLayers;
   alert("TEST2")
   map.addLayers([activityarea]);
  }
  
  
});
 
    map.on("layer-remove", layerremove)
 
 function layerremove()
 {
  TemplatePicker.destroy()
  
 }
 
   
      
//   map.addLayers([activityarea]);

        function initEditor(evt) {
     


           var templateLayers = arrayUtils.map(evt.layers, function(result){
      
   return result.layer;
   

          });


 // TEST: Changed to stewardship instead of templateLayers

          var templatePicker = new TemplatePicker({
            featureLayers: templateLayers,
            grouping: true,
            rows: "auto",
            columns: 3
          }, "templateDiv");
          templatePicker.startup();

          var layers = arrayUtils.map(evt.layers, function(result)
     {
     
   
   var fieldInfos= arrayUtils.map(result.layer.fields,function(field){
            if(field.name === 'RecipientLast'){
              return {'fieldName': field.name,'label':'Rec Last Name'}
            }
   
    if(field.name === 'RecipientFirst'){
              return {'fieldName': field.name,'label':'Rec First Name'}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   
   if(field.name === 'Phone'){
              return {'fieldName': field.name,'label':'Phone', 'customField':myDijit}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   
   if(field.name === 'ZipCode'){
              return {'fieldName': field.name,'label':'ZipCode', 'customField':myDijit2}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   if (field.name == 'DateStart')
   {
    
       return {'fieldName': field.name,'label':'Start Date', 'customField': datedijit }
    
   }
            else{
              return {'fieldName': field.name,'label':field.alias}
            }
          });
            return { featureLayer: result.layer, 'fieldInfos':fieldInfos};
          });
 
 
      
        //add a default value for newly added features 
     
          var settings = {
            map: map,
            templatePicker: templatePicker,
            layerInfos: layers,
            toolbarVisible: true,
            createOptions: {
              polylineDrawTools:[ Editor.CREATE_TOOL_FREEHAND_POLYLINE ],
              polygonDrawTools: [ Editor.CREATE_TOOL_FREEHAND_POLYGON,
                Editor.CREATE_TOOL_CIRCLE,
                Editor.CREATE_TOOL_TRIANGLE,
                Editor.CREATE_TOOL_RECTANGLE
              ]
            },
            toolbarOptions: {
              reshapeVisible: true,
     mergeVisible: true,
     cutVisible: true
     
     
     
            }
   
   
          };

          var params = {settings: settings};    
          var myEditor = new Editor(params,'editorDiv');
          //define snapping options
          var symbol = new SimpleMarkerSymbol(
            SimpleMarkerSymbol.STYLE_CROSS, 
            15, 
            new SimpleLineSymbol(
              SimpleLineSymbol.STYLE_SOLID, 
              new Color([255, 0, 0, 0.5]), 
              5
            ), 
            null
          );
          map.enableSnapping({
            snapPointSymbol: symbol,
            tolerance: 20,
            snapKey: keys.ALT
          });

        }//end of init editor function

          
dojo.connect(dijit.byId("Activity"), 'onChange', function(value){ 
               deferred.resolve("success");
              return deferred.promise;
     
  
    
   }


       });
0 Kudos
JianHuang
Deactivated User
First of all, since JS API 3.4 (cannot remember the exact version number), you don't have to wait for map onload to create an editor widget. You can call new Editor(); right after var map = new Map();
Secondly, editor widget doesn't provide a method setLayerInfos() to allow you to dynamically change the editing layers. What you need to do is to destroy the editor widget and recreate it every time user select a new item in the dropdown.
Hope this helps.
0 Kudos
ionarawilson1
Deactivated User
Hi Jian,

Can you please take a look at my code and tell me how I destroy the template and remove the layer? It is not working. When I select another item in the combobox, I would like to have the layer removed and the editor destroyed but I don't know how to do that. I kind of need the layers-add-result event, because to create layerinfos for the editor settings, I need the results of the event. Thank you so much!!!

    dojo.connect(dijit.byId("Activity"), 'onChange', function(value){ 
             //alert('ok ' + event);
 
    if (value in oc(officeactivitylist)) {
    
      map.on("layers-add-result", initEditor);
  var stewardship = new FeatureLayer("http://tfsgis-iisd01:6080/arcgis/rest/services/MyMapService2/FeatureServer/0",{
          mode: FeatureLayer.MODE_ONDEMAND, 
          outFields: ['*']
        });
  
    map.addLayers([stewardship]);
 
        function initEditor(evt) {
 
          var templateLayers = arrayUtils.map(evt.layers, function(result){
            return result.layer;
 

          });

          var templatePicker = new TemplatePicker({
            featureLayers: templateLayers,
            grouping: true,
            rows: "auto",
            columns: 3
          }, "templateDiv");
          templatePicker.startup();

          var layers = arrayUtils.map(evt.layers, function(result) {
     
   
   var fieldInfos= arrayUtils.map(result.layer.fields,function(field){
            if(field.name === 'RecipientLast'){
              return {'fieldName': field.name,'label':'Rec Last Name'}
            }
   
    if(field.name === 'RecipientFirst'){
              return {'fieldName': field.name,'label':'Rec First Name'}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   
   if(field.name === 'Phone'){
              return {'fieldName': field.name,'label':'Phone', 'customField':myDijit}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   
   if(field.name === 'ZipCode'){
              return {'fieldName': field.name,'label':'ZipCode', 'customField':myDijit2}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   if (field.name == 'DateStart')
   {
    
       return {'fieldName': field.name,'label':'Start Date', 'customField': datedijit }
    
   }
            else{
              return {'fieldName': field.name,'label':field.alias}
            }
          });
            return { featureLayer: result.layer, 'fieldInfos':fieldInfos};
          });
 


 
          var settings = {
            map: map,
            templatePicker: templatePicker,
            layerInfos: layers,
            toolbarVisible: true,
            createOptions: {
              polylineDrawTools:[ Editor.CREATE_TOOL_FREEHAND_POLYLINE ],
              polygonDrawTools: [ Editor.CREATE_TOOL_FREEHAND_POLYGON,
                Editor.CREATE_TOOL_CIRCLE,
                Editor.CREATE_TOOL_TRIANGLE,
                Editor.CREATE_TOOL_RECTANGLE
              ]
            },
            toolbarOptions: {
              reshapeVisible: true,
     mergeVisible: true,
     cutVisible: true
     
     
     
            }
   
   
          };

          var params = {settings: settings};    
          var myEditor = new Editor(params,'editorDiv');
          //define snapping options
          var symbol = new SimpleMarkerSymbol(
            SimpleMarkerSymbol.STYLE_CROSS, 
            15, 
            new SimpleLineSymbol(
              SimpleLineSymbol.STYLE_SOLID, 
              new Color([255, 0, 0, 0.5]), 
              5
            ), 
            null
          );
          map.enableSnapping({
            snapPointSymbol: symbol,
            tolerance: 20,
            snapKey: keys.ALT
          });
    
   
      
        }
    
   }
    
   if (value  in oc(communityactivitylist)) {
        alert("Community Activity")
     map.removeLayer(stewardship)
     myEditor.destroy()
     
   }
   
   if (value  in oc(countyactivitylist)) {
   
     alert("County Activity")
   }
   if (value in oc(activityarealist)) {
   
     alert("Activity Area")
   }
   if (value  in oc(activitypointlist)) {
   
     alert("Activity Point")
   }
   
   else if (value == "Forest Stewardship Plan") {
   
     alert("Stewardship")
   }
       });
    
0 Kudos
JianHuang
Deactivated User
After you call editor.destroy();, you can recreate it by editor = new Editor(params, 'editorDiv'); just make sure params.settings.layerInfos exclude the layer that you removed from the map.
0 Kudos
ionarawilson1
Deactivated User
Hi Jian,

Sorry for the delay in replying. I got sidetracked with another issue. I am able to remove the first layer when I click on the other combobox but the editor is still showing the previous layer. How do I make sure params.settings.layerInfos exclude the layer that I removed from the map?

    if (value in oc(officeactivitylist)) {
     
    
    var contentHolder = "<div id='templateDiv'></div><div id='editorDiv'></div>";
    dojo.place(contentHolder, "contentpane", "first");
  
    
      map.on("layers-add-result", initEditor);

  
    map.addLayers([stewardship]);
 
        function initEditor(evt) {
 
          var templateLayers = arrayUtils.map(evt.layers, function(result){
            return result.layer;
 

          });

          var templatePicker = new TemplatePicker({
            featureLayers: templateLayers,
            grouping: true,
            rows: "auto",
            columns: 3
          }, "templateDiv");
          templatePicker.startup();

          var layers = arrayUtils.map(evt.layers, function(result) {
     
   
   var fieldInfos= arrayUtils.map(result.layer.fields,function(field){
            if(field.name === 'RecipientLast'){
              return {'fieldName': field.name,'label':'Rec Last Name'}
            }
   
    if(field.name === 'RecipientFirst'){
              return {'fieldName': field.name,'label':'Rec First Name'}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   
   if(field.name === 'Phone'){
              return {'fieldName': field.name,'label':'Phone', 'customField':myDijit}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   
   if(field.name === 'ZipCode'){
              return {'fieldName': field.name,'label':'ZipCode', 'customField':myDijit2}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   if (field.name == 'DateStart')
   {
    
       return {'fieldName': field.name,'label':'Start Date', 'customField': datedijit }
    
   }
            else{
              return {'fieldName': field.name,'label':field.alias}
            }
          });
            return { featureLayer: result.layer, 'fieldInfos':fieldInfos};
          });
 


 
          var settings = {
            map: map,
            templatePicker: templatePicker,
            layerInfos: layers,
            toolbarVisible: true,
            createOptions: {
              polylineDrawTools:[ Editor.CREATE_TOOL_FREEHAND_POLYLINE ],
              polygonDrawTools: [ Editor.CREATE_TOOL_FREEHAND_POLYGON,
                Editor.CREATE_TOOL_CIRCLE,
                Editor.CREATE_TOOL_TRIANGLE,
                Editor.CREATE_TOOL_RECTANGLE
              ]
            },
            toolbarOptions: {
              reshapeVisible: true,
     mergeVisible: true,
     cutVisible: true
     
     
     
            }
   
   
          };

          var params = {settings: settings};    
          var myEditor = new Editor(params,'editorDiv');
          //define snapping options
          var symbol = new SimpleMarkerSymbol(
            SimpleMarkerSymbol.STYLE_CROSS, 
            15, 
            new SimpleLineSymbol(
              SimpleLineSymbol.STYLE_SOLID, 
              new Color([255, 0, 0, 0.5]), 
              5
            ), 
            null
          );
          map.enableSnapping({
            snapPointSymbol: symbol,
            tolerance: 20,
            snapKey: keys.ALT
          });
    
        myEditor.startup();
      
        }
    
   }
    
   if (value  in oc(communityactivitylist)) {
   
    map.removeLayer(stewardship)
              
    
  map.on("layers-add-result", initEditor2);

  
    map.addLayers([activityarea]);
 
        function initEditor2(evt) {
 
          var templateLayers = arrayUtils.map(evt.layers, function(result){
            return result.layer;
 

          });

          var templatePicker = new TemplatePicker({
            featureLayers: templateLayers,
            grouping: true,
            rows: "auto",
            columns: 3
          }, "templateDiv");
          templatePicker.startup();

          var layers = arrayUtils.map(evt.layers, function(result) {
     
   
   var fieldInfos= arrayUtils.map(result.layer.fields,function(field){
            if(field.name === 'RecipientLast'){
              return {'fieldName': field.name,'label':'Rec Last Name'}
            }
   
    if(field.name === 'RecipientFirst'){
              return {'fieldName': field.name,'label':'Rec First Name'}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   
   if(field.name === 'Phone'){
              return {'fieldName': field.name,'label':'Phone', 'customField':myDijit}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   
   if(field.name === 'ZipCode'){
              return {'fieldName': field.name,'label':'ZipCode', 'customField':myDijit2}
    //    return {'fieldName': field.name,'label':'Rec First Name',stringFieldOption:esri.dijit.AttributeInspector.STRING_FIELD_OPTION_TEXTAREA
            }
   if (field.name == 'DateStart')
   {
    
       return {'fieldName': field.name,'label':'Start Date', 'customField': datedijit }
    
   }
            else{
              return {'fieldName': field.name,'label':field.alias}
            }
          });
            return { featureLayer: result.layer, 'fieldInfos':fieldInfos};
          });
 


 
          var settings2 = {
            map: map,
            templatePicker: templatePicker,
            layerInfos: layers,
            toolbarVisible: true,
            createOptions: {
              polylineDrawTools:[ Editor.CREATE_TOOL_FREEHAND_POLYLINE ],
              polygonDrawTools: [ Editor.CREATE_TOOL_FREEHAND_POLYGON,
                Editor.CREATE_TOOL_CIRCLE,
                Editor.CREATE_TOOL_TRIANGLE,
                Editor.CREATE_TOOL_RECTANGLE
              ]
            },
            toolbarOptions: {
              reshapeVisible: true,
     mergeVisible: true,
     cutVisible: true
         
     
            }
   
   
          };
  myEditor.destroy()
          var params2 = {settings: settings2};    
          var myEditor2 = new Editor(params2,'editorDiv');
          //define snapping options
          var symbol = new SimpleMarkerSymbol(
            SimpleMarkerSymbol.STYLE_CROSS, 
            15, 
            new SimpleLineSymbol(
              SimpleLineSymbol.STYLE_SOLID, 
              new Color([255, 0, 0, 0.5]), 
              5
            ), 
            null
          );
          map.enableSnapping({
            snapPointSymbol: symbol,
            tolerance: 20,
            snapKey: keys.ALT
          });
    
        myEditor2.startup();
      
        }
0 Kudos
ionarawilson1
Deactivated User
I keep getting an error

Uncaught Error: Tried to register widget with id==btnNewSelection but that id is already registered

I think it had to do with not destroying the editor, but how can I destroy an editor from another function since it is not available on the other function? Thanks
0 Kudos
JianHuang
Deactivated User
To answer the question how to get the reference of editor object in another function. You can:
1. declare editor as a global variable, which is not recommended in most cases since it may cause conflicts.
2. use dojo.hitch to pass extra arguments to the callback function. Read the doc:
https://dojotoolkit.org/reference-guide/1.7/dojo/hitch.html#providing-arguments
0 Kudos
ionarawilson1
Deactivated User
Jiang, I don't know how to create a function when declaring the editor variable, since the constructor does not accept other arguments. Can you please check and let me know what I need to change? Do I need to use any require (import) statement?

Here is the snippet for the function where the user an item in the combobox:
   var params = {settings: settings};    
         var myEditor = new Editor(params,'editorDiv');
    var func = dojo.hitch(myEditor, function(){
  myEditor.destroy();
});


And here I am calling the function

if (displayedvalue in oc(communityactivitylist)) {
    
    map.removeLayer(stewardship)
                dojo.destroy("templateDiv")
    dojo.destroy('editorDiv')
    
    map.destroy()
    
    var contentHolder = "<div id='templateDiv2'></div><div id='editorDiv2'></div>";
    dojo.place(contentHolder, "contentpane", "first");
 
        map = new Map("map", { 
          basemap: "hybrid",
         sliderStyle: "large",

   
          center: [-98.57, 30.98],
          zoom: 6,
          slider: true 


        });
    
  
    map.addLayers([activityarea]);
  map.setExtent(map.extent); 
        func()


Of course I get an error:  Cannot call method 'toJson' of undefined.

Can you please help me with this? Thank you!
0 Kudos