Can't get form/button events from inside a popup, version 4.6

2560
9
Jump to solution
01-24-2018 11:01 AM
DanAncona
New Contributor II

I'm trying to get a small form (for adding comments) working in a popup. To do this, I'm trying to add a dom event listener when the popup is visible, but getElementById (aka byId) can't find the button element. This tries to add it whether the popup is visible or not, but you get the idea -

  view.popup.watch("visible", function(visible) {
    console.log("popup visible: ", visible);
    var btn;
    try {
      //view.popup.renderNow(); // seems to do bupkis
      btn = dom.byId("btnAddComment");
      console.log("watch btn is ", btn);
      on(dom.byId("btnAddComment"), "click", function() {
        console.log("clicky");
      });
    } catch (err) {
      console.log(btn, err);
    }
  });
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The template is super simple, based on the popup intro example -

  var template = { // autocasts as new PopupTemplate()
    title: "Marriage in NY, Zip Code: {ZIP}",
    content: "<button id='btnAddComment'>Add Comment</button>"
  };
‍‍‍‍

I've got a codepen of this if you want to see the rest of the code. Pretty sure I did something like this in 3.x, really not sure why it's not working here. Thanks!

Tags (4)
1 Solution

Accepted Solutions
MattDriscoll
Esri Contributor

The best solution would be to set the popupTemplate content to a function that returns a domNode instead of an HTML string. Since the Popup uses a virtual DOM, the node isn't guaranteed to be there when you're calling getElementByID(). If you create a function that returns a domNode you can isten to events on the domNode in the function.

something like the following

myFunction(graphic) {

  var node = document.createElement("div");

  node.onClick = doSomething();

  return node

}

popupTemplate = {

  content: myFunction

};

View solution in original post

9 Replies
RobertScheitlin__GISP
MVP Emeritus

Dan,

  It is just a timing issue.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>Intro to PopupTemplate - 4.6</title>

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

  <link rel="stylesheet" href="https://js.arcgis.com/4.6/esri/css/main.css">
  <script src="https://js.arcgis.com/4.6/"></script>

  <script>
    require([
      "esri/Map",
      "esri/layers/FeatureLayer",
      "esri/views/MapView",
      "dojo/on",
      "dojo/dom",
      "dojo/domReady!"
    ], function(
      Map,
      FeatureLayer,
      MapView,
      on, dom
    ) {

      // Create the map
      var map = new Map({
        basemap: "gray"
      });

      // Create the MapView
      var view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-73.950, 40.702],
        zoom: 11
      });

      /*************************************************************
       * The PopupTemplate content is the text that appears inside the
       * popup. {fieldName} can be used to reference the value of an
       * attribute of the selected feature. HTML elements can be used
       * to provide structure and styles within the content. The
       * fieldInfos property is an array of objects (each object representing
       * a field) that is use to format number fields and customize field
       * aliases in the popup and legend.
       **************************************************************/

       var template = { // autocasts as new PopupTemplate()
         title: "Marriage in NY, Zip Code: {ZIP}",
         content: "<button id='btnAddComment'>Add Comment</button>"
       };

      // Reference the popupTemplate instance in the
      // popupTemplate property of FeatureLayer
      var featureLayer = new FeatureLayer({
        url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/ArcGIS/rest/services/NYCDemographics1/FeatureServer/0",
        outFields: ["*"],
        popupTemplate: template
      });
      map.add(featureLayer);

      view.popup.watch("visible", function(visible) {
        console.log("popup visible: ", visible);
        try {
          setTimeout(function(){
            btn = dom.byId("btnAddComment");
            console.log("watch btn is ", btn);
            on(dom.byId("btnAddComment"), "click", function() {
              console.log("clicky");
            });
          }, 200);
        } catch (err) {
          console.log(err);
        }
      });
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>
DanAncona
New Contributor II

Hm, not working for me. I was suspecting some kind of a race condition, ergo the fooling around with .renderNow(). Seems like there must be some way to get an event when the form is done rendering and added to the page DOM, but, I didn't see a "ready" state or anything like that. Thanks for looking at it though!

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Dan strange. The above code worked great for me.

0 Kudos
DanAncona
New Contributor II

OK, this is very weird - tried re-pasting it into that codepen, and it's working now for me too. The arbitrary race condition thing still seems not ideal, but this is one solution at least. Thanks so much!

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Sure thing.

Don't forget to mark this question as answered by clicking on the "Mark Correct" link on the reply that answered your question.

0 Kudos
KellyHutchins
Esri Frequent Contributor

Have you considered using the popup actions for this? Seems like it would be the simplest approach. 

Popup actions | ArcGIS API for JavaScript 4.6 

0 Kudos
DanAncona
New Contributor II

Yeah, I poked around with these. Would have been perfect except I don't want the button at the top, I wanted it under a comment form.

0 Kudos
MattDriscoll
Esri Contributor

The best solution would be to set the popupTemplate content to a function that returns a domNode instead of an HTML string. Since the Popup uses a virtual DOM, the node isn't guaranteed to be there when you're calling getElementByID(). If you create a function that returns a domNode you can isten to events on the domNode in the function.

something like the following

myFunction(graphic) {

  var node = document.createElement("div");

  node.onClick = doSomething();

  return node

}

popupTemplate = {

  content: myFunction

};

DanAncona
New Contributor II

This put me on the right path. Seems a lot more robust than relying on a timeout. Here's working code, including some domConstruct features I didn't know about -

        function makeTemplate(graphic){
          var node = domConstruct.toDom("<div>I'm a Node</div>");
          var btn = domConstruct.create("button", { id: 'myBtn', innerHTML: "And I'm a button" }, node, "last");
          btn.onclick = function() { console.log(graphic.graphic.attributes); }
          return node;
        }

Big thanks to everyone who looked at this.