Popup, PopupTemplate, and InfoTemplate clear as mud

1333
7
Jump to solution
03-21-2019 06:42 AM
MollyFoley
Regular Contributor

What is the difference between these three? Do you have to define a popup object before you define a popup template? My understanding is that the popup template is the same as an info template, the only difference is that the popupTemplate lets you use graphs/pictures and do extra formatting. I think when I define a feature layer and it's properties, I can use a popupTemplate for the infoTemplate property, correct? Is myPopupTemplate.setContent() the same method as myInfoTemplate.setContent()? I create all the content for the popup dynamically, so I can't use fieldInfos or anything like that in the PopupTemplate. This is also why I can't use a "click" function on a feature that brings up the InfoTemplate, because if there are overlapping features (on the same feature layer) and a user uses the arrows on the popup, the click function won't fire again, thus the InfoTemplate does not update.

This leaves me with a select-change event on a Popup object (if the select-change event does not fire when the arrows are used, then this whole question is moot and there is no solution to using arrows which would be dumb)...but I don't understand how that correlates with PopupTemplate/InfoTemplate/InfoWindow. Popup objects have their own setContent method. Do I not need a PopupTemplate or an InfoTemplate and instead just set the content and title using the Popup object's own methods? 

This is my current code using an InfoTemplate. Note that the content for the InfoTemplate is fired on click of a feature and arrows on the popup do not act as a click. It would be great if there was a solution to the arrow problem while still using InfoTemplates so then I wouldn't have to mess with popup/popupTemplate.

var mgmtTractPopupBox = new InfoTemplate();

//Add the habitat management tract feature layer
var hbMgmtTractFL = new FeatureLayer("https://xxx/rest/services/HabitatMonitoring/HabitatData/MapServer/3", {
    refreshInterval: 10,
    mode: FeatureLayer.MODE_ONDEMAND,
    visible: false,
    outFields: ["*"],
    infoTemplate: mgmtTractPopupBox
});
hbMgmtTractFL.setMinScale(500000);
hbMgmtTractFL.setSelectionSymbol(selectionSym);

//Format mgmtTract popup content on click of a feature
hbMgmtTractFL.on("click", function (feature) {
    var OID = feature.graphic.attributes.OBJECTID;
    relatedTractAttrb(OID);
});
        
function relatedTractAttrb(oids) {
    var relate = new RelationshipQuery();
    relate.objectIds = [oids];
    relate.relationshipId = 0;
    relate.outFields = ['*'];
    hbMgmtTractFL.queryRelatedFeatures(relate, function (fset) {
        var features = fset[oids].features;
            var rows = ['<table id="mgmtPopupTable1"><tr><th>Veg Practice</th><th>Herbicide</th><th>Month Treated</th>\
                    <th>Year Treated</th><th>Implemented By</th><th>Funded By</th><th>Farm Bill Code</th></tr>'];
            for (i = 0; i < features.length; i++) {
                var feature = features[i]
                var vegPractice = vegPName(feature.attributes.VegMgmtPractice);
                var herbicide = herbName(feature.attributes.Herbicide);
                var monthTreated = monthName(feature.attributes.MonthTreated);
                var yearTreated = feature.attributes.YearTreated;
                var impBy = impName(feature.attributes.ImplementedBy);
                var fundBy = fundName(feature.attributes.FundedBy);
                var fbc = feature.attributes.FarmBillCode;
                if (fundBy == "CRP" || fundBy == "CRP - CREP") {
                    fbc = crpName(fbc);
                }
                else if (fundBy == "EQIP" || fundBy == "EQIP - RCPP") {
                    fbc = eqipName(fbc);
                }
                else {
                    fbc = "Not applicable";
                }
                var contentRow = '<tr><td>' + vegPractice + '</td><td>' + herbicide + '</td><td>' + monthTreated + '</td><td>' + yearTreated +
                    '</td><td>' + impBy + '</td><td>' + fundBy + '</td><td>' + fbc + '</td></tr>';
                rows.push(contentRow);
            }
        content = rows.join("");
        content = content + '</table>';
        mgmtTractPopupBox.setTitle("Habitat Management Tract ${OBJECTID}");
        mgmtTractPopupBox.setContent(content);
    });
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The documentation on all three of these things is incredibly confusing and I wish ESRI would clean it up

1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Esteemed Contributor

Molly,

   Wow that is a lot of questions. Let me try and clear some things up for you.

  1. What is the difference between these three?
    • Popup is a singleton dijit that a map has by default to show info in a bubble type dialog.
    • PopupTemplate is a object that is used by the maps popup to format and extract certain info from a feature that has been set in the popups features property.
    • InfoTemplate is the first generation of a template object that does what a PopupTemplate does but a little less.
  2. Do you have to define a popup object before you define a popup template?
    • No a Map object by default has a Popup dijit as its infoWindow property.
  3. My understanding is that the popup template is the same as an info template, the only difference is that the PopupTemplate lets you use graphs/pictures and do extra formatting. I think when I define a feature layer and it's properties, I can use a PopupTemplate for the InfoTemplate property, correct?
    • You are absolutely correct InfoTemplate is the old object and almost always you would want to use a PopupTemplate.
    • Now if you are setting your own content directly to the popup using setContent method, then there is no need for a template at all. Templates (either one) are just a means to pre-define how the attributes of the selected feature should be displayed.
  4. Is myPopupTemplate.setContent() the same method as myInfoTemplate.setContent()?
    • Basically Yes.

I create all the content for the popup dynamically, so I can't use fieldInfos or anything like that in the PopupTemplate. This is also why I can't use a "click" function on a feature that brings up the InfoTemplate, because if there are overlapping features (on the same feature layer) and a user uses the arrows on the popup, the click function won't fire again, thus the InfoTemplate does not update.

In your use case there is no need at all to define the layer infoTemplate as you never use it. There is no need for you to use it either.

Do you see a case when you will click on the map and have overlapping features?

If so you can set the popups feature property using the setFeatures method and then just use the selection-change event to determine the graphic that is going to be used when calling the map.infoWindow.setContent method.

Hope this helps.

View solution in original post

7 Replies
RobertScheitlin__GISP
MVP Esteemed Contributor

Molly,

   Wow that is a lot of questions. Let me try and clear some things up for you.

  1. What is the difference between these three?
    • Popup is a singleton dijit that a map has by default to show info in a bubble type dialog.
    • PopupTemplate is a object that is used by the maps popup to format and extract certain info from a feature that has been set in the popups features property.
    • InfoTemplate is the first generation of a template object that does what a PopupTemplate does but a little less.
  2. Do you have to define a popup object before you define a popup template?
    • No a Map object by default has a Popup dijit as its infoWindow property.
  3. My understanding is that the popup template is the same as an info template, the only difference is that the PopupTemplate lets you use graphs/pictures and do extra formatting. I think when I define a feature layer and it's properties, I can use a PopupTemplate for the InfoTemplate property, correct?
    • You are absolutely correct InfoTemplate is the old object and almost always you would want to use a PopupTemplate.
    • Now if you are setting your own content directly to the popup using setContent method, then there is no need for a template at all. Templates (either one) are just a means to pre-define how the attributes of the selected feature should be displayed.
  4. Is myPopupTemplate.setContent() the same method as myInfoTemplate.setContent()?
    • Basically Yes.

I create all the content for the popup dynamically, so I can't use fieldInfos or anything like that in the PopupTemplate. This is also why I can't use a "click" function on a feature that brings up the InfoTemplate, because if there are overlapping features (on the same feature layer) and a user uses the arrows on the popup, the click function won't fire again, thus the InfoTemplate does not update.

In your use case there is no need at all to define the layer infoTemplate as you never use it. There is no need for you to use it either.

Do you see a case when you will click on the map and have overlapping features?

If so you can set the popups feature property using the setFeatures method and then just use the selection-change event to determine the graphic that is going to be used when calling the map.infoWindow.setContent method.

Hope this helps.

MollyFoley
Regular Contributor

That is helpful, thank you. I have other feature layers that use InfoTemplates; they are truly being formatted with stuff in curly brackets so I think those I do need to define, unlike the one seen above.

So, to use the select-change event and setFeatures() I need to create a Popup object. BUT, I still need to use a click event to be able to differentiate between layers. The only layer that uses a click event is the layer seen above to bring up the popup since the content has to be calculated every time due to the related feature query. The others are InfoTemplates and nothing needs to be calculated, they are pre-defined before the layer is added to the map. So when I click on features in the layer where they overlap, only one feature is retrieved; whatever the map designates as the one on "top."

hbMgmtTractFL.on("click", function(feature {});‍‍‍‍‍‍

I guess my problem is that I don't understand how to use setFeatures(). If I set the features on the popup, I think it would only be setting the single feature that is on top when the overlapping features are clicked. 

hbMgmtTractFL.on("click", function(feature) {
   myPopup.setFeatures(feature.graphic);
   var OID = feature.graphic.attributes.OBJECTID;
   //call function to calculate content and do call map.infoWindow.setContent()
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This code currently makes the InfoWindow/Popup flash when I click the overlapping features. It also  highlights all the geometry associated with the event point, when I just want it to highlight one feature at a time. I have not yet attempted to implement select-change because I'm not sure where I would call it.

//Replace infoWindow with popup
var popup = new Popup({
    fillSymbol = selectionSym
}, domConstruct.create("div"));

map = new Map("map", {
    basemap: "hybrid",
    center: [-85.603281, 36.241294],
    zoom: 6,
    infoWindow: popup
});

//Add the habitat management tract feature layer
var hbMgmtTractFL = new FeatureLayer("https://xxx/rest/services/HabitatMonitoring/HabitatData/MapServer/3", {
    refreshInterval: 10,
    mode: FeatureLayer.MODE_ONDEMAND,
    visible: false,
    outFields: ["*"]
});
hbMgmtTractFL.setMinScale(500000);
hbMgmtTractFL.setSelectionSymbol(selectionSym);

//Format mgmtTract popup content on click of a feature
hbMgmtTractFL.on("click", function (feature) {
    //Does setFeatures() go here somewhere?
    //popup.setFeatures(feature.graphic)  <-- Only one feature though.
    var OID = feature.graphic.attributes.OBJECTID;
    relatedTractAttrb(OID);
});
        
function relatedTractAttrb(oids) {
    var relate = new RelationshipQuery();
    relate.objectIds = [oids];
    relate.relationshipId = 0;
    relate.outFields = ['*'];
    hbMgmtTractFL.queryRelatedFeatures(relate, function (fset) {
        //do stuff to create content
        map.infoWindow.setTitle("Habitat Management Tract " + oids[0]);
        map.infoWindow.setContent(content);
    });
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

It works for the features with InfoTemplates defined perfectly. It's just the hbMgmtTractFL feature layer that I can't figure out, and I know it has something to do with that fact that the template is set in the click function... Clicking on other feature layers with InfoTemplates

:

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

Molly,

  You should reconsider your workflow. If you assign a popuptemplate for the hbMgmtTractFL and remove your click event on it, then you can just listen for the map.infoWindow set-features event and if the layer equals your hbMgmtTractFL then you can do your relationship query and set the content of the popup otherwise just let the popup widget do its own thing (for the other layers). Same thing for listening for the select-change event examine if the feature is from your specific layer and if so then do your query.

0 Kudos
MollyFoley
Regular Contributor

Edit: we were writing at the same time. Will read your response...... Now I've read it. That was my thought too, on a select-change event I should be able to see what layers are coming through. I can't access the features to determine the layer on a select-change event though, see below in my response. 

Does anything get returned by "selection-change"? Does not appear that it returns features or graphics. I also can't seem to access popup object properties, which I don't quite understand. For example, if I click on a feature in the map and look at the popup object in the console, I can see it has a features property (just as it is noted in the documentation). If I try to access the features property doing something like so:

var features = popup.features;
console.log(features);‍‍‍‍‍‍

I just get "undefined."

If I try to access the count property where I can see the popup has a count of 3 features, the count comes back as 0.

console.log(popup) //The count property displays "3"
var count = popup.count
console.log(count) //The count displays "0"‍‍‍‍‍‍‍‍‍‍‍‍

If I try to access the isShowing property of the popup, I get false even though the popup is displaying on the map.

var visible = popup.isShowing
console.log(visible);‍‍‍‍‍‍

The only way I can do anything on a selection-change event is if I can access those features... there's also a million other issues such as my hbMgmtTractFL not even being recognized as a layer on the map by the popup object unless the InfoTemplate is set.  If the infoTemplate is set, then the features in that FL actually highlight, if it isn't only the background feature highlights (which is also a featureLayer with an infoTemplate set). A popup set-features event also is not accessible for determining layers, just tried it.

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

Molly,

   Sounds like your popup is not the actual map.infoWindow then.

You need to be using

var feature = map.infoWindow.getSelectedFeature();

method to get the currently selected feature in the popup.

0 Kudos
MollyFoley
Regular Contributor

Thank you Robert, that finally got me where I needed to be. You've answered countless questions from me on this community, I do sincerely appreciate your patience!

If you wouldn't mind explaining more, how could my popup not be the actual map.infoWindow when I define it before I instantiate my map? Just trying to understand this better.

//Replace infoWindow with Popup object
var popup = new Popup({
    fillSymbol: new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,
        new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
            new Color([255, 0, 0]), 2), new Color([255, 255, 0, 0.25])),
}, domConstruct.create("div"));

//Instantiate the map
map = new Map("map", {
    basemap: "hybrid",
    center: [-85.603281, 36.241294],
    zoom: 6,
    infoWindow: popup
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The other question I have (sorry, I'm full of questions); is there anyway to avoid running the select-change event twice when you click on features that overlap? 


If I click where the green dot is, the selection-change event runs through twice, once for each feature, I assume because both features technically get selected, even though it only highlights one. The last feature that runs through the selection-change event is the one that gets highlighted, so the content in the popup shows as I would expect it to. Unfortunately, there isn't a .getSelectedFeatures() method that would return an array of selected features, so I don't think I can get just the one considered "on top" by using indexes. Not a huge deal as everything works fine, just wondering if I can avoid unnecessary processing somehow.

This ended up being the final code for the popup select-change event.

popup.on("selection-change", function () {
    var feature = popup.getSelectedFeature();
    if (feature != undefined && feature._graphicsLayer.name == "MgmtTracts") {
        var OID = feature.attributes.OBJECTID;
        relatedTractAttrb(OID);
    }
});‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

Molly,

   That is strange. I am not sure why you popup was not being recognized as the popup in your code. But unless you were attempting to do something special with the popup (like put it in a panel) there is no need for you to create your own popup as the map object by default already has a Popup dijit as its infoWindow property.

Unfortunately no there is no way to have the selection-change only fire once. It will fire as many times are it finds features and even when there are no features found because the selection is changed from something to nothing. What I would do is check the popup.location and if it is identical to the first time the selection-change method was called then bail out of the function.