Multiple services in Identify javascript API

2048
3
06-01-2011 03:03 PM
SusanMcclendon
New Contributor III
I'm trying to implement identify across multiple services (with multiple layers).  I'm having trouble with the asynchronous processing of the identify task.  I've seen sucessful code in silver light/flex api, but can't seem to make it work in the javascript api.

I've tried a couple of different approaches by using the dojo.deferred object returned by the identify task or breaking out the callbacks into a separate functions, but I'm apparently dense on how to control the flow of execution using dojo.  Any suggestions are appreciated. 

My test at the end of the forEach service loop, tab container hasChildren(), is ALWAYS false, even though I can see results in the content pane object and the tab container object are correct.  I understand why it is happening, but not versed enough in javascript in general to know how to fix the issue and process synchronously.

Thanks,

Susan

>>execution order
>>1
>>3
>>2

console.log("execution order");
console.log("1");                                     

dojo.forEach(loadedServices, function(service) {
    mapService = map.getLayer(service);
    layerids.length = 0;                                                                    //reset layerids array
    
    if (mapService.visibleLayers.length > 0) {                                      //does service have visible layers?
        url = mapService.url;                                                             //get url of service
        identifyTask = new esri.tasks.IdentifyTask(url);                          //ceate new identify task 
        identifyParams.layerIds = mapService.visibleLayers;                    //other params set in init function
            
        //var idDeferred = identifyTask.execute(identifyParams);              //tried using return deferred object
        //idDeferred.then(function(idResults) {                                     //results the same, not right approach

        identifyTask.execute(identifyParams,
            function(idResults) {
                
                //populate object for service and visible layers
                if (idResults.length>0) {
                    console.log("2");                                                      //execution order, understand it is async
                    
                    cp = new dijit.layout.ContentPane ({                          //create a new tab for each service results
                    title: service,
                    style: "height:100px; width: 300px; color:#000000 background-image:none;background-color:transparent;"
                    });
                    addToCP(idResults);                                               //process results into contentPane
                    
                    tc.addChild(cp);                                                    //add contentPane to tab container 
                    console.log(tc.hasChildren());                                  //is true....
                }
            },
            
            function(err) {
                console.log("doIdentify: (" + err.name + ") " + err.message);
            });
    }

});

console.log("3");
console.log(tc);

if (tc.hasChildren()) {                                                    //is always false due to 
tc.startup();
    map.infoWindow.setContent(tc.domNode);
    map.infoWindow.show(evt.screenPoint, map.getInfoWindowAnchor(evt.screenPoint));
} else {
    alert('No results found, please check layer visibility and try again.');
}
3 Replies
AndrewJones
New Contributor
Javascript is single threaded so you must remeber that a the function chain you are in must finish before any other functions are executed, even those fired by an async callback.  Your dojo.foreach is in the same function as your test of the tc.hasChildren therefore you will always hit the hasChildren test before any identify results callbacks are executed.

You will need to count back in your identify tasks in the callback function and run the code in the tc.hasChildren 'if' block when all have returned.
0 Kudos
SusanMcclendon
New Contributor III
Thank you, and thanks to our other Java developer for pointing me in the right direction.  I was looking in the wrong direction for some property/method that would let me know when all the deferreds completed.  I had tried using deferredList, and I think that might work, but in my attempt deferredList would work on the first pass, but fail on all mouse clicks that followed.

Sometimes it is hard to see the forest thru the trees and keep in mind KISS.

Thanks,

Susan

working code below:

//thanks David for help on handling tracking of callback completion
var callback_counter=0;
var expected_callback_counter=0;
dojo.forEach(loadedServices, function(service) {
    mapService = map.getLayer(service);
    if (mapService.visibleLayers.length > 0) {              
        expected_callback_counter++;                        //every visible layer should produce a callback, even if no results
    }
});

//for each loaded non-base layer service
dojo.forEach(loadedServices, function(service) {
    mapService = map.getLayer(service);                     //get service object
    layerids.length = 0;
    
    if (mapService.visibleLayers.length > 0) {              //does service have visible layers?
        showLoading();
        url = mapService.url;                               //get url of service
        identifyTask = new esri.tasks.IdentifyTask(url);    //ceate new identify task
        identifyParams.layerIds = mapService.visibleLayers; //set layers to identify

        identifyTask.execute(identifyParams,
            function(idResults) {                           //callback function - fires for each visible layer
                if (idResults.length>0) {                   //create tab for service if results returned
                    cp = new dijit.layout.ContentPane ({
                    title: service,
                    style: "height:150px; width: 400px; color:#000000 background-image:none;background-color:transparent;"
                    });
                    
                    //dojo.forEach (allResults, function(result) {allResults.push(result)});  //may use different display, need allResults at once
                    addToMap(idResults);                    //process results into containter content
                    
                    tc.addChild(cp);
                }
                
                callback_counter++;                         //a callback has fired - count them
                
                if (callback_counter == expected_callback_counter) {
                    if (tc.hasChildren()) {                 //may have no results across all services
                        tc.startup();
                        map.infoWindow.setContent(tc.domNode);
                        hideLoading();
                        map.infoWindow.show(evt.screenPoint, map.getInfoWindowAnchor(evt.screenPoint));
                        //addToMap2(allResults);
                    }
                    else {
                        alert('No results found!');
                    }
                }
            },
            
            function(err) {                                 //error callback
                console.log("doIdentify: (" + err.name + ") " + err.message);
            }
0 Kudos
deleted-user-z9OjN-EoUVWn
New Contributor II

Even though this is an old post, I just want to say how much it helped me today. Absolutely outstanding!  I did have to massage this code lightly to get it to work.  I added some logic to only identify visible map service layers.  Also, rather than creating a custom content box for the response, I was able to use map.infoWindow setFeatures and show methods to use the default map info window.  Additionally, I tied it all to a map click event.  Of course, I will need to take additional time to format info Templates for each layer. Here is my code:

 map.on("click", function (event) {

    //Identify tasks
    let identifyParams = new IdentifyParameters();
    identifyParams.tolerance = 3;
    identifyParams.returnGeometry = true;
    identifyParams.width = map.width;
    identifyParams.height = map.height;
    identifyParams.mapExtent = map.extent;
    identifyParams.geometry = event.mapPoint;

    let callback_counter = 0;
    let expected_callback_counter = 0;
    dojo.forEach(loadedMapServices, function (service) {
      let mapService = map.getLayer(service);
      if (mapService.visibleLayers.length > 0) {
        expected_callback_counter++; //every visible layer should produce a callback, even if no results  
      }
    });

    //for each loaded non-base layer service  
    let deferred = [];
    dojo.forEach(loadedMapServices, function (service) {

      let mapService = map.getLayer(service); //get service object  

      if (mapService.visible) {
        if (mapService.visibleLayers.length > 0) { //does service have visible layers?  
          // showLoading();  
          let url = mapService.url; //get url of service  
          let identifyTask = new IdentifyTask(url); //ceate new identify task  
          identifyParams.layerIds = mapService.visibleLayers; //set layers to identify  

          let deferred_idTask = identifyTask
            .execute(identifyParams)
            .addCallback(function (response) {
              // response is an array of identify result objects
              // Let's return an array of features.
              return arrayUtils.map(response, function (result) {
                let feature = result.feature;
                let desc = result.layerName;
                console.log(feature);
                let Template = new InfoTemplate(desc, "${*}");
                feature.setInfoTemplate(Template);
                return feature;
              });
            });
          deferred.push(deferred_idTask);
        }
      }
    });

    map.infoWindow.setFeatures(deferred);
    map.infoWindow.show(event.mapPoint);
  });
  
});
0 Kudos