Can you specify an OnClick event specific to a Layer to identify two different Popups

7036
11
01-25-2013 06:29 AM
KristenJones1
Occasional Contributor
I'm trying to figure out how to create a different InfoWindow (template) popup for each of my feature layers, since each have a different set of fields and data.  I have not been able to find any examples yet, but I figured that I could start by creating a different OnClick event. So for example dojo.connect(map, "onClick", myClickHandler);  can I do something like

dojo.connect(map(FeatureLayer1), "onClick", myClickHandler);

dojo.connect(map(FeatureLayer2), "onClick", myClickHandler);

or am I way off on how to proceed with this?

THANKS!
0 Kudos
11 Replies
SteveCole
Honored Contributor
It certainly is possible. First, you'll need to define infoTemplates for each feature layer:

 var template1 = new esri.InfoTemplate();
 template1.setContent(SetFeatureLayer1PopupInfo); //call function to set the content
 template1.setTitle('Some generic title or another function');

 var template2 = new esri.InfoTemplate();
 template2.setContent(SetFeatureLayer2PopupInfo); //call function to set the content
 template2.setTitle('Some generic title or another function');


Now, when you create your feature layers, you just choose the appropriate infoTemplate and then do the dojo.connect as usual:

theFeatureLayer1 = new esri.layers.FeatureLayer("REST Url", {
mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
infoTemplate: template1,
outFields: ["*"],
visible: true
});

theFeatureLayer2 = new esri.layers.FeatureLayer("REST Url", {
mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
infoTemplate: template2,
outFields: ["*"],
visible: true
});


dojo.connect(theFeatureLayer1, "onClick", function(evt) {
     var query = new esri.tasks.Query();
     query.geometry = pointToExtent(map,evt.mapPoint,10);
     
     var deferred = theFeatureLayer1.selectFeatures(query,esri.layers.FeatureLayer.SELECTION_NEW);
     map.infoWindow.resize(550,350);
     map.infoWindow.setFeatures([deferred]);
     map.infoWindow.show(evt.mapPoint);
});


Later in your code will be the function to create the popup content:

function SetFeatureLayer1PopupInfo(graphic) {
     fullAttr = graphic.attributes;
     someval = fullAttr.FIELDNAME;

     content = '<table><tr><td>Field Name:</td><td>' + someval + '</td><tr></table>';

     return content;
}
0 Kudos
KristenJones1
Occasional Contributor
That was the same line of thinking I had, however the dojo.connect event does not work changing it from "map" to the Layer name

If you see line 118      dojo.connect(map, 'onClick', function (e) {


If I change this to      dojo.connect(permits, 'onClick', function (e) {

It does not work. I was thinking I could do a "map.permits" but that does not work either.. 



<!DOCTYPE html>
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=7,IE=9">
    <!--The viewport meta tag is used to improve the presentation and behavior
    of the samples on iOS devices-->
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
    <title>Permit Locator</title>

    <link rel="stylesheet" href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.3/js/dojo/dijit/themes/claro/claro.css">
    <link rel="stylesheet" href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.3/js/esri/css/esri.css">
    <style>
      html, body {
        height: 100%;
        width: 100%;
        margin: 0;
        padding: 0;
      }
      #map {
        padding:0;
      }
    </style>

    <script>var dojoConfig = { parseOnLoad: true };</script>
    <script src="http://serverapi.arcgisonline.com/jsapi/arcgis/3.3/"></script>
    <script>
      dojo.require("dijit.layout.BorderContainer");
      dojo.require("dijit.layout.ContentPane");
      dojo.require("esri.map");
      dojo.require("esri.layers.FeatureLayer");
      dojo.require("esri.dijit.Popup");
      
      var map;
      var permits;
      var properties;
      
      function init() {
      
      var sfs = new esri.symbol.SimpleFillSymbol(esri.symbol.SimpleFillSymbol.STYLE_SOLID, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([111, 0, 255]), 2), new dojo.Color([111, 0, 255, 0.15]));
      
       
       var popup = new esri.dijit.Popup({
          fillSymbol: sfs
        }, dojo.create("div"));
        

        map = new esri.Map("map",{
          basemap: "streets",
          center: [-84.183, 39.734],
          zoom: 13,
          infoWindow: popup
        });
        
        dojo.addClass(map.infoWindow.domNode, "myTheme");

       
      //Add all the permits and parcels to see on load   
      var operationalLayer = new esri.layers.ArcGISDynamicMapServiceLayer("http://map.miamiconservancy.org:6080/arcgis/rest/services/MCDPropertiesGeoDB/MapServer", {"opacity":0.9});
      map.addLayer(operationalLayer);
         
        
      //apply a popup template to the permits layer to format popup info 
      var popupTemplate = new esri.dijit.PopupTemplate({
          title: "{Permits.MASTERUSER.%PERMIT.ISSUEDTO}",
          fieldInfos: [{
            fieldName: "Permits.MASTERUSER.%PERMIT.MCDNUM",
            label: 'Permit Number (MCDNUM):',
            visible: true
          }, {
            fieldName: "Permits.MASTERUSER.%PERMIT.LOCATION",
            label: "Location:",
            visible: true
          }]
        });
        
        
        
        //apply a popup template to the MCD Parcels layer to format popup info 
        var popupTemplateParcels = new esri.dijit.PopupTemplate({
          title: "{MCDID}",
          fieldInfos: [{
            fieldName: "MCDID",
            label: 'MCD ID:',
            visible: true
          }, {
            fieldName: "PARCELID",
            label: "Parcel ID:",
            visible: true
          }]
        });

            

       //add the permits layer to the map as a feature layer in selection mode we'll use this layer to query and display the selected permits
        permits = new esri.layers.FeatureLayer("http://map.miamiconservancy.org:6080/arcgis/rest/services/MCDPropertiesJOINToSQL/MapServer/2", {
          outFields: ["*"],
          infoTemplate: popupTemplate,
          mode: esri.layers.FeatureLayer.MODE_SELECTION
        });
        
        
        //add the Properties layer to the map as a feature layer in selection mode
        properties = new esri.layers.FeatureLayer("http://map.miamiconservancy.org:6080/arcgis/rest/services/MCDPropertiesJOINToSQL/MapServer/0", {
          outFields: ["*"],
          infoTemplate: popupTemplateParcels,
          mode: esri.layers.FeatureLayer.MODE_SELECTION
        });




          permits.setSelectionSymbol(sfs);
          
          properties.setSelectionSymbol(sfs);


        //when users click on the map select the permit using the map point and update the url parameter
        dojo.connect(map, 'onClick', function (e) {
          var query = new esri.tasks.Query();
          query.geometry = e.mapPoint;
          var deferred = permits.selectFeatures(query, esri.layers.FeatureLayer.SELECTION_NEW, function (selection) {
            //update the url param if a permit was located
            if (selection.length > 0) {
              var mcdnum = selection[0].attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"];
              //Refresh the URL with the currently selected permit
              if (typeof history.pushState !== 'undefined') {
                window.history.pushState(null, null, "?mcdnum=" + selection[0].attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"]);
              }
            }
          });
          map.infoWindow.setFeatures([deferred]);
          map.infoWindow.show(e.mapPoint);
        });




       dojo.connect(map, 'onLayersAddResult', function (result) {
          // Add a link into the InfoWindow Actions panel       
          var emailLink = dojo.create("a", {
            "class": "action",
            "innerHTML": "Email Map",
            "href": "javascript:void(0);"
          }, dojo.query(".actionList", map.infoWindow.domNode)[0]);


          // Register a function to be called when the user clicks on
          // the above link
          dojo.connect(emailLink, "onclick", function (evt) {
            var feature = map.infoWindow.getSelectedFeature();
            var url = window.location;
            var emailLink = "mailto:?subject=Permit Map of :" + feature.attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"]+ "&body=Check out this permit: %0D%0A " + window.location;
            window.location.href = emailLink;
          });

          //When users navigate through the history using the browser back/forward buttons select appropriate permit  
          //https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history
          window.onpopstate = function (event) {
            var mcdnum = getPermitFromUrl(document.location.href);
            if (mcdnum) {
              selectPermit(mcdnum);
            } else {
              permits.clearSelection();
              map.infoWindow.hide();
            }
          };

          //if a mcdnum is specified in url param select that feature 
          var mcdnum = getPermitFromUrl(document.location.href);
          selectPermit(mcdnum);
        });

        map.addLayers([permits]);
        map.addLayers([properties]);
      }

      //extract the permit id from the url
      function getPermitFromUrl(url) {
        var urlObject = esri.urlToObject(url);
        if (urlObject.query && urlObject.query.mcdnum) {
          return urlObject.query.mcdnum;
        } else {
          return null;
        }
      }

      //select permit from the feature layer by creating a query to look for the input mcdnum id 
      function selectPermit(mcdnum) {
        if (mcdnum) {
          var query = new esri.tasks.Query();
          query.where = "Permits.MASTERUSER.%PERMIT.MCDNUM = " + mcdnum + "";
          //query.where = "Permits.MASTERUSER.%PERMIT.MCDNUM = 2963";
          var deferred = permits.selectFeatures(query, esri.layers.FeatureLayer.SELECTION_NEW, function (selection) {
            var center = esri.graphicsExtent(selection).getCenter();
            var extHandler = dojo.connect(map, 'onExtentChange', function () {
              dojo.disconnect(extHandler);
              //zoom to the center then display the popup 
              map.infoWindow.setFeatures(selection);
              map.infoWindow.show(center);
            });
            map.centerAt(center);
          });
        }
      }









      
      dojo.ready(init);
          </script>
  </head>
  
  <body class="claro">
    <div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline',gutters:false" 
    style="width: 100%; height: 100%; margin: 0;">
      <div id="map" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'" style="border:1px solid #000;padding:0;"></div>
    </div>
  </body>
</html>
 
0 Kudos
SteveCole
Honored Contributor
Try using a function outside of the dojo.connect code block instead of an inline function? Maybe the code is "running over" the deferred part.

I've had this happen to me with other aspects of using the javascript API. At any rate, I'm using this technique in the apps I'm working on and it works for me.

[EDIT] Noticed that you're using popup templates. My example and my background has been with infoTemplates so I don't know if that's making any difference..
0 Kudos
DianaBenedict
Frequent Contributor
You need to change the code below:

var deferred = permits.selectFeatures(query, esri.layers.FeatureLayer.SELECTION_NEW, function (selection) {
            //update the url param if a permit was located
            if (selection.length > 0) {
              var mcdnum = selection[0].attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"];
              //Refresh the URL with the currently selected permit
              if (typeof history.pushState !== 'undefined') {
                window.history.pushState(null, null, "?mcdnum=" + selection[0].attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"]);
              }
            }
          });
          map.infoWindow.setFeatures([deferred]);
          map.infoWindow.show(e.mapPoint);
        });

To something like this:

var deferred = permits.selectFeatures(query, esri.layers.FeatureLayer.SELECTION_NEW);

deferred.then(function (results) {
//add all you code here that you want to run after the selectFeatures async call is done...
//in your case, I believe that results is what you will want to pass to the map.infoWindow.setFeatures method, but I would step through the code and eval the results object to make sure!
}

Note that the dojo documentation has a pretty good explanation of using deffered.then, you can even hook up a call when the deffered call fails/errors

If you don't want to mess with deffereds then you could do something like this (I think though I haven't tested)

       permits.selectFeatures(query, esri.layers.FeatureLayer.SELECTION_NEW, function (selection) {
            //update the url param if a permit was located
            if (selection.length > 0) {
              var mcdnum = selection[0].attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"];
              //Refresh the URL with the currently selected permit
              if (typeof history.pushState !== 'undefined') {
                window.history.pushState(null, null, "?mcdnum=" + selection[0].attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"]);
              }
              map.infoWindow.setFeatures(selection); //you may or may not need the brakets as so [selection]
              map.infoWindow.show(e.mapPoint);
            }
          });
        });

You just have to be sure that you defer any action until you get a callback from the server indicating that the selection is complete and you have gotten the results (just as the previous person said).

Diana
0 Kudos
KristenJones1
Occasional Contributor
Thank you both for taking the time to look at my problem!

I'm still getting my feet wet with this API and Javascript for that matter, so bare with me.

So Diana, are you saying I should try the two separate click events based on the Layer name but adding your code to the "PErmits" layer?  or maintain the Dojo.connect(map, .... and plug your code in?  I'd rather plug code snippits that try to figure out the correct syntax right now, and learn as I go along.

How does this look (complete) ? Since I have two layers with different fields and data, I'm thinking I need to still seperate the OnClick based on the Layer

dojo.connect(permits, 'onClick', function (e) 
 {
          var query = new esri.tasks.Query();
          query.geometry = e.mapPoint;

          permits.selectFeatures(query, esri.layers.FeatureLayer.SELECTION_NEW, function (selection) 
      {
          //update the url param if a permit was located

          if (selection.length > 0) 
            {
            var mcdnum = selection[0].attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"];
             //Refresh the URL with the currently selected permit
               if (typeof history.pushState !== 'undefined') 
                {
                 window.history.pushState(null, null, "?mcdnum=" + selection[0].attributes["Permits.MASTERUSER.%PERMIT.MCDNUM"]);
                }
       map.infoWindow.setFeatures(selection);
       map.infoWindow.show(e.mapPoint);
            }
      }
 });

// Now for the Properties layer



    dojo.connect(properties, 'onClick', function (e) 
{
          var query = new esri.tasks.Query();
          query.geometry = e.mapPoint;
          map.infoWindow.setFeatures(selection);  
          map.infoWindow.show(e.mapPoint);
});





Thanks again for taking your time to help!

Jason
0 Kudos
DianaBenedict
Frequent Contributor
Jason, from a quick glance it looks like your sample code should work.  I guess it really depends on how you want to manage your code and what the business logic is.  If the business logic implies that when a user clicks on the map they should be able to select from either feature layer then you might want to consider a map onClick event by where you create 1 query object uisng the point geometry and then query both feature layers.  I actually do this in my code so that users can click or draw a bounding rectangle around the aerea of interest and perform multiple async queries to the server, gather up the results and process as needed.  Implementing this method can complicate matters and makes the learning curve a little more difficult however it can make the code a little cleaner and less redundant. 

With that said, you also need to evaluate what happens if a user clicks on the map and it is close to both feature layers? Do both events get called?  Does one callback overwrite the results of the other?  I have not researched this possiblity when it comes to hooking up multiple callbacks to featurelayer onClick events. Connecting callbacks and managing them correctly tends to be a pain, but once it is done successfully, can make your job much easier.
Are you able to debug put breaks in your code so that you can see when events fire?  doing this will help you determine the best approach and eval where potential bugs could be introduced. 

I would suggest you start of by putting in a break or alert within each one of the dojo.connect(layer1, onClick,...) and dojo.connect(layer1, onClick,...) anonymous functions to see how it all plays out when you click on the map.  If both are getting fired then you might want to consider using the map onClick event instead and manage selecting from both featurelayers and then process the selections seperately.

Let me know how it all plays out and then we can discuss the best approach.  I hope it all makes sense
0 Kudos
KevinMacLeod1
Frequent Contributor
I actually have the same question. I want to use popupTemplate for to create an "Identify" for all our layers, like in ArcMap. Only when they are visible of course. For all features, all layers. Or at least top feature of all layers would do for now. But so that it has that little "arrow" next to the close button in the popup Infowindow that lets us page through all the layers (example would be 1 of 3, where there are 3 coincident points that Identify finds and puts in the popup.)

Hopefully this method can be extended for not just Feature Layers (most of our layers are Feature) but also Dynamic Layers. But based on a few other threads with samples it seems like we have to handle Dynamic differently than Feature?

I just want popups like ArcGIS.com viewer provides. They "just work"! Identifies all features, any type of layer. It seems like all the various samples here have to be "glued" together to make this "Identify" functionality or tool.

I recommend to ESRI they consider including this in the next Basic Viewer template: a robust, configurable Identify tool. (And hierarchical Table of Contents (like ArcMap or AGSTOC) and Query Widget.)

For now, I've just coded in a popup InfoWindow for every layer manually and enabled it with over a dozen blocks of the following:

dojo.connect(LayerName, "onClick", function (evt) {
            map.infoWindow.setFeatures([evt.graphic]);
            map.infoWindow.show(evt.mapPoint);
        });

This doesn't provide the little arrow that pages through multiple layers (should it, out of the box, with popup dijit?) that are coincident and doesn't work on Dynamic layers. Also there is a better way with some looping code I have not implemented yet.

I will experiment with the code examples above soon. Thank you Diana, dcastano and evtguy for posting!
0 Kudos
DianaBenedict
Frequent Contributor
Jason forgot to mention a couple of other things:

1) make sure to read the topic on popups and templateInfo in the help docs
http://help.arcgis.com/en/webapi/javascript/arcgis/jshelp/#intro_popuptemplate also look up the one for InfoTemplates
2) check out the samples on popups and InfoTemplates - note that you might only really need InfoTemplates to start of with and then work your way to popups as needed. It looks like you will want to set up the templateInfo for each feature layer in the portion of the code where you intantiate each feature layer (as suggested by Steve). Note that the template Info object allows you to specifiy a place holder for the fields by using something like:  "here I will add some text about "  {yourfieldname} " and then I can add more custum text here that also appends the info from " {anotherfield} ...etc...
3) I noticed that you are using SelectionMode for the feature layer so the layer onClick event may not even ever fire since the layer is just an image not a dowloaded graphic.
4) I use SelectionMode becuase I am dealing with nation wide data and want to control what graphics actually get streamed to the client side from the server. This means that I always have to perform selection queries on my data in order to process/display attributes, etc...
5) finally, don't foget to add the layers to the map .. if you are using SelectionMode then you will also need to add the MapServer portion of the feature services so the users can actually see the data displayed as an image

I hope this helps!

Diana
0 Kudos
KristenJones1
Occasional Contributor
Thanks again Diana for the help,  I don't suppose you are available for freelance work, at least help enough to get my page working the way I need and then to learn from the code?  🙂
0 Kudos