4.12 Popup changes: why? (and what now?..)

2785
9
08-30-2019 12:36 AM
by Anonymous User
Not applicable

I'd like to question your decision to drop the 'content: '*'' for the popup template ... I won't be hiding my anger and disappointment, because I feel like with each release you just want to p..s us off. 

I'd really, really like to know why you dropped the support for simply telling the template do render based on whatever attributes are available for the feature.

And now, I'd really like someone to explain or provide a code that will assign each identified feature from the identify tasks a template using your latest api (4.12), where you have no layer available, but still want to provide all the info available in the attributes, which was easy with '*' before

Thank you

this.esriLib.all(tasks).then(resultsArray => {
      const features = [];
      resultsArray.forEach((results, idx) => {
        if (results && results.results) {
          const l = lyrs[idx];

          // Loop through features and embed the feature its layer
          results.results.forEach((item) => {
            const feature = item.feature;
            if (!feature) {
              return;
            }
            // Set the symbol. 
            feature.symbol = this.mapService.getIdentifyResultSymbol(feature.geometry);

            // Set the layer.
            // feature.layer = l;
            feature.popupTemplate = new this.esriLib.PopupTemplate({
              title: item.layerName || l.title,
              content: ?????
            });

            features.push(feature);
          });
        }
      });

      if (features && features.length > 0) {
        // Now setup the content for the popup
        const popup = this.mapView.popup;
        popup.features = features;
        popup.open({
          features: features,
          location: point
        });
        console.log(popup);
      }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
...
0 Kudos
9 Replies
RobertScheitlin__GISP
MVP Emeritus

Michal,

  So did you see this note in the breaking changes documentation?

Starting with version 4.12, PopupTemplate content can no longer be set using a wildcard, e.g. *. Instead, set the defaultPopupTemplateEnabled property to true.

0 Kudos
by Anonymous User
Not applicable

Hi Robert,

yes I've seen it and I'm merely asking why they had to remove it. sure I can provide my own function that will render a table, but I have to hack it to look like the default esri one. and now with every version change I need to make sure it's the same all the time... sigh.

That parameter 'defaultPopupTemplateEnabled' is fine but works for featureLayers, but this is a result from the Identify, that doesn't have a layer at all, so the popup comes up with the blank content. (has a title, but no content).

The way I see it I cannot make the esri to auto-populate a popup content for the features coming from the identify result ...

ReneRubalcava
Frequent Contributor

You can specify the fields you want to display in your popup content.

PopupTemplate | ArcGIS API for JavaScript 4.12 

This lets you be explicit about the fields to show. It's a little more code then "*", but still works very well.

Here is a demo https://codepen.io/odoe/pen/VwZzjgB?editors=1000 

feature.popupTemplate = {
  title: "{Map Unit Name}",
  content: [
    {
      type: "fields",
      // labels not needed in this case
      // since alias field name returned
      // by default with Identify
      fieldInfos: [
        {
          fieldName: "Map Unit Name",
          label: "Map Unit Name"
        },
        {
          fieldName: "Dominant Order",
          label: "Dominant Order"
        },
        {
          fieldName: "Dominant Sub-Order",
          label: "Dominant Sub-Order"
        }
      ]
    }
  ]
};

This also lets you control the ordering of the fields in the table.

by Anonymous User
Not applicable

guys, even though I'm thankful for your help, but providing the fields is not an option:

- there are possibly 20+ layers to identify

- providing the fields ('hardcoding') is not ideal due to possible schema changes

- brings unnecessary overhead for maintaining possibly hundreds of layers with the field and field infos (though this could be extracted from the layer fieldInfos but again, another complexity to the code).

the '*' thing is something I'm pretty sure lots of people and developers use as:

- it provides a clean way of informing the api use the default api

- it guarantees the uniformity between popups

- there is no requirement to know anything about the fields

The only thing I can do is to create a helper function that will create a template content that will mimic the default one, but this has a major downside too:

- with each API change there is a high probability the design of the default content template will change, so I will have to update the code as well.

Again, I would advise on taking a considerate approach if you plan on 'breaking' things to something that was around since version 1.x

Thank you and with regards

by Anonymous User
Not applicable

I've resolved it by assigning the template with the content of type: 'fields' and re-mappign the attributes object. Lame but it kind of works:

// loop through the identify results, get feature, assign its symbol and push to collection of features for the popup
          results.results.forEach((item) => {
            const feature = item.feature;
            if (!feature) {
              return;
            }
            // Set the symbol.
            feature.symbol = this.mapService.getIdentifyResultSymbol(feature.geometry);

            const l = lyrs[item.layerId];
            // Prepare the popup template
            const template = new this.esriLib.PopupTemplate({
              title: item.layerName || l.title,
              content: [{
                type: 'fields',
                fieldInfos: Object.keys(feature.attributes).map(k => {
                  return {
                    fieldName: k,
                    label: k
                  };
                })
              }]
            });
            feature.popupTemplate = template;
            features.push(feature);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
by Anonymous User
Not applicable

Michal Gasparovic I agree. Do you have a complete demo or site using this workaround you created? Actually I just looked at IdentifyTask. It doesn't support dynamic layers? I guess I need a QueryTask. I just want a popup with the layer title, showing all the fields and I'll figure out filtering out Object ID and Shape later. I'm just starting 4.x.

My next task will be making the popup moveable again. Easy in 3.x with dojo. I found this but it uses IdentifyTask though it looks like it's using a MapServer but I guess the trick is to use all layer IDs for the sublayers. https://quarticsolutions.com/tutorial/draggable-popups-with-angular-and-arcgis-api-for-javascript-4-...  I will definitely be putting together a writeup and working Popup demo on all this as it is such a common need. All popups on, all fields (minus Oid and shape), and moveable are my goals. 

0 Kudos
by Anonymous User
Not applicable

Hi Kevin, see below:

basically what it does is:

  • sets the default symbol (in case the LayerInfo request fails)
  • gets the LayerInfo (async)
  • organizes the fields from LayerInfo
  • from the layer info it gets all the Symbol for the feature from the renderer (async op, round trip to server needed)
  • if polygon, sets the transparency to the symbol (to make sure it doesn't hide anything behind)
  • creates the template
  • adds the feature with the updated template to the result array. that is then used in the Popup

private processLayerResults(results, idx, lyrs): Promise<any> {
    return new Promise(async (resolve) => {
      const features = [];
      if (results && results.results) {

        // idx is the index in the lyrs object. so get the layer that this results set is coming from
        const l: esri.MapImageLayer = lyrs[idx];

        // Loop through features and embed the feature its layer
        for (let i = 0; i < results.results.length; i++) {
          const item = results.results[i];
          const feature = item.feature;
          if (!feature) {
            return;
          }

          // This is the sublayer.
          const subLayer: esri.Sublayer = l.findSublayerById(item.layerId);

          // This is the default identify symbol
          feature.symbol = this.mapService.getIdentifyResultSymbol(feature.geometry);

          if (subLayer) {
            // Now get the proper value for the objectid attribute and then get the renderer.
            const {fields, drawingInfo} = await this.mapService.getLayerInfo(subLayer.url);
            if (fields) {
              const oidField = this.mapService.getObjectIdField(fields);
              // rewrite the value to number (if possible)
              if (feature.attributes[oidField]) {
                try {
                  feature.attributes[oidField] = Number(feature.attributes[oidField]);
                }
                catch (e) {
                  // ignore
                }
              }
            }
            if (drawingInfo && drawingInfo.renderer) {
              const renderer = this.getRendererFromJson(drawingInfo.renderer);
              if (renderer) {
                const symbol = await this.mapService.getSymbolFromRenderer(renderer, feature, fields);
                if (symbol) {
                  feature.symbol = symbol;
                }
              }
            }
          }

          if (feature.geometry.type === 'polygon') {
            const hasFill = feature.symbol.color && feature.symbol.color.a !== 0;
            if (!hasFill) {
              feature.symbol.color.a = 0.1;
            }
          }

          // Prepare the popup template
          const template = new this.esriLib.PopupTemplate({
            title: item.layerName,
            content: [{
              type: 'fields',
              fieldInfos: Object.keys(feature.attributes).map(k => {
                return {
                  fieldName: k,
                  label: k
                };
              })
            }]
          });

          feature.popupTemplate = template;
          feature.layer = this.identifyLayer;
          feature.sourceLayer = this.identifyLayer;
          features.push(feature);
        }

        resolve(features);
      }
    });
  }

  private processResults(resultsArray, lyrs): Promise<any> {
    return new Promise(async resolve => {
      try {
        const features = [];
        for (let idx = 0; idx < resultsArray.length; idx++) {
          const results = resultsArray[idx];
          features.push(await this.processLayerResults(results, idx, lyrs));
        }
        resolve(features);
      }
      catch (e) {
        resolve([]);
      }
    });
  }

      this.processResults(resultsArray, lyrs).then(features => {
        features = [].concat.apply([], features);
        if (features && features.length > 0) {
          this.identifyLayer.addMany(features);
          this.mapView.popup.open({
            features: features,
            location: evt.mapPoint
          });
        }

        // And finish it
        this.stopPending(features && features.length);
        }
      }).catch(error => {
        this.stopPending();
        this.showInstructions();
      });
    });
by Anonymous User
Not applicable

Hi Michal Gasparovic I mostly follow your concept, I am trying ..it has been a while since I needed to delve this deep past WebApp Builder! 

I was wondering if you thought the above approach would be necessary if we use the defaultPopupTemplateEnabled = true?  I was thinking maybe I might make the jump to featureLayers (from map image) for my viewers.  So I was thinking I could loop through a MapServer endpoint and add all the subLayers with  MapImageLayer.allSublayers ?   What do you think?   My understanding is defaultPopup does what the wildcard does.  Next - I have to get the popup moveable again.. I hope Dojo Moveable dnd works in 4x.

If featureLayers don't work, Noah Sager noted this idea of all fields should also be possible in 4.15+ using a map image layer JS 4.14 - MapImageLayer sublayers default popup template I suppose we would still need to gather all allSublayers into an array for this. I'll update everyone if I get it working.

0 Kudos
KevinMacLeod3
New Contributor III

In the related thread above by Noah he notes that the ability for showing all fields for a MapImage layer did not make the cut for API version 4.16 and potentially not 4.17. I tried featureLayer, and it also seems adding as a featureLayer also will not show all fields with the  defaultPopupTemplateEnabled : true flag.  

Please pardon me if I'm missing something here. I'm new to 4x! 

Rene Rubalcava‌  Example with this flag for a featurelayer from a service sublayer, it doesn't work, no attibutes show up:  https://codepen.io/kevinsagis/details/oNxZeXL (click on map)

If I specify particular fields, they work fine.

https://codepen.io/kevinsagis/details/BaKWdKr 

Hopefully soon popups will show all fields with this flag so schemas are not hardcoded/breakable, perhaps even with an option to show data if not null would be cool. 

0 Kudos