How do I get a table of Feature Layer attributes to show in a Grid?

2924
4
Jump to solution
04-09-2020 12:26 AM
MasonBindl
New Contributor II

I'm having trouble getting a grid element to display the data from a feature layer. I've been modifying the answer to this questionDisplay attribute table with JavaScript API ver 4.9?  by Robert Scheitlin, GISP‌. 

 Lidia Dudina‌ did you get a feature layer to work with Robert's example?

I"m getting a Type Error on line 293 in the createGrid function. Not sure where I'm going wrong here.

Any help would be greatly appreciated. 

Thanks,
Mason

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <title>dGrid - 4.9</title>

  <link rel="stylesheet" href="https://js.arcgis.com/4.14/esri/css/main.css">
  <script src="https://js.arcgis.com/4.9/"></script>
  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
      overflow: hidden;
    }

    #info,
    #gridDisplay {
      position: absolute;
      bottom: 0;
      left: 0;
      height: 35%;
      background-color: white;
      border-color: grey;
      width: 100%;
      font-family: "Avenir Next W00", "Helvetica Neue", Helvetica, Arial, sans-serif;
      font-size: 14px;
    }

    #info {
      z-index: 90;
      font-size: 16px;
      padding-left: 20px;
    }

    #info * {
      padding-right: 20px;
    }

    #gridDisplay {
      z-index: 80;
    }

    .info {
      line-height: 20px;
      padding-left: 5px ! important;
    }

    .dgrid-header,
    .dgrid-header-row {
      background-color: #eee;
      color: #57585A;
    }

    .dgrid-row-even {
      background-color: #F7F8F8;
    }

    .dgrid-row-odd {
      background-color: #EFEFEF;
    }

    .dgrid-selected {
      background: #B4DAF5;
    }

    .dgrid-row {
      border: none
    }
  </style>

  <script>
    require([
        "esri/config",
        "esri/WebMap",
        "esri/views/MapView",
        "esri/layers/FeatureLayer",
        "esri/Graphic",
        "esri/widgets/Legend",
        "esri/widgets/Expand",
        "esri/geometry/SpatialReference",
        "dgrid/OnDemandGrid",
        "dgrid/extensions/ColumnHider",
        "dojo/store/Memory",
        "dstore/legacy/StoreAdapter",
        "dgrid/Selection"
      ],
      function(
        esriConfig, WebMap, MapView, FeatureLayer, Graphic, Legend, Expand, SpatialReference,
        OnDemandGrid, ColumnHider, Memory, StoreAdapter, Selection
      ) {
        {
        esriConfig.portalUrl = "https://maps.trpa.org/portal";
            };
                
        let map, view, meadowLayerView, grid;
        const gridDiv = document.getElementById("grid");
        const infoDiv = document.getElementById("info");

        // create new map, view and csvlayer
        const gridFields = ["OBJECTID", "MeadowType", "Final_Rating",
           "Acres"
        ];
        
        const meadowLayer = new FeatureLayer({
            url: "https://maps.trpa.org/server/rest/services/SEZViewer_AssessmentUnits_Meadows/MapServer/0",
            outFields: gridFields
        });


        map = new WebMap({
            portalItem: {
              id: "6d740280f5e34698bae774b38cde6469"
            },
            layers: [meadowLayer]
          });

            
        view = new MapView({
            container: "viewDiv",
            map: map,
            highlightOptions: {
              color: "#2B65EC",
              fillOpacity: 0.4
            },
            padding: {
              bottom: infoDiv.clientHeight
            }
          });

          const legendExpand = new Expand({
            view: view,
            content: new Legend({
              view: view,
              style: "card"
            })
          });
          view.ui.add(legendExpand, "top-left");


        // create a new datastore for the on demandgrid
        // will be used to display attributes of selected features
        const dataStore = new StoreAdapter({
          objectStore: new Memory({
            idProperty: "OBJECTID"
          })
        });
        /****************************************************
         * Selects features from the layer that intersect
         * a polygon that user drew using sketch view model
         ****************************************************/
        function popGrid() {
          view.graphics.removeAll();
          if (meadowLayerView) {
            const query = {
              where: "1=1",
              outFields: ["*"]
            };

            // query graphics from the layer view. Geometry set for the query
            // can be polygon for point features and only intersecting geometries are returned
            meadowLayerView.queryFeatures(query).then(function(results) {
                const graphics = results.features;
                // if the grid div is displayed while query results does not
                // return graphics then hide the grid div and show the instructions div
                if (graphics.length > 0) {
                  gridDiv.style.zIndex = 90;
                  infoDiv.style.zIndex = 80;
                  document.getElementById("featureCount").innerHTML =
                    "<b>Showing attributes for " +
                    graphics.length.toString() + " features </b>"
                } else {
                  gridDiv.style.zIndex = 80;
                  infoDiv.style.zIndex = 90;
                }

                // get the attributes to display in the grid
                const data = graphics.map(function(feature, i) {
                  return Object.keys(feature.attributes)
                    .filter(function(key) {
                      // get fields that exist in the grid
                      return (gridFields.indexOf(key) !== -1);
                    })
                    // need to create key value pairs from the feature
                    // attributes so that info can be displayed in the grid
                    .reduce(function(obj, key) {
                      obj[key] = feature.attributes[key];
                      return obj;
                    }, {});
                });

                // set the datastore for the grid using the
                // attributes we got for the query results
                dataStore.objectStore.data = data;
                grid.set("collection", dataStore);
              })
              .catch(errorCallback);
          }
        }
        /************************************************
         * fires when user clicks a row in the grid
         * get the corresponding graphic and select it
         *************************************************/
        function selectFeatureFromGrid(event) {
          // close view popup if it is open
          view.popup.close();
          // get the ObjectID value from the clicked row
          const row = event.rows[0]
          const id = row.data.OBJECTID;

          // setup a query by specifying objectIds
          const query = {
            objectIds: [parseInt(id)],
            outFields: ["*"],
            returnGeometry: true,
            outSpatialReference: view.SpatialReference
          };

          // query the csvLayerView using the query set above
          meadowLayerView.queryFeatures(query).then(function(results) {
              const graphics = results.features;
              // remove all graphics to make sure no selected graphics
              view.graphics.removeAll();
              view.goTo(graphics[0].geometry);

              // create a new selected graphic
              const selectedGraphic = new Graphic({
                geometry: graphics[0].geometry,
                symbol: {
                  type: "simple-marker",
//                  style: "circle",
                  color: "orange",
                  size: "12px", // pixels
                  outline: { // autocasts as new SimpleLineSymbol()
                    color: [255, 255, 0],
                    width: 2 // points
                  }
                }
              });

              // add the selected graphic to the view
              // this graphic corresponds to the row that was clicked
              view.graphics.add(selectedGraphic);
            })
            .catch(errorCallback);
        }

        /************************************************
         * Creates a new grid. Loops through layer 
         * fields and creates grid columns
         * Grid with selection and columnhider extensions
         *************************************************/
        function createGrid(fields) {
          var columns = fields.filter(function(field, i) {
            if (gridFields.indexOf(field.name) !== -1) {
              return field;
            }
          }).map(function(field) {
            if (field.name === "OBJECTID") {
              return {
                field: field.name,
                label: field.name,
                sortable: true,
                hidden: true
              };
            } else {
              return {
                field: field.name,
                label: field.alias,
                sortable: true
              };
            }
          });

          // create a new onDemandGrid with its selection and columnhider
          // extensions. Set the columns of the grid to display attributes
          // the hurricanes cvslayer
          grid = new(OnDemandGrid.createSubclass([Selection, ColumnHider]))({
            columns: columns
          }, "grid");

          // add a row-click listener on the grid. This will be used
          // to highlight the corresponding feature on the view
          grid.on("dgrid-select", selectFeatureFromGrid);
        }
        
        function errorCallback(error) {
          console.log("error:", error)
        }
        
        // create a grid with given columns once the layer is loaded
        meadowLayer.when(function() {
            // create a grid with columns specified in gridFields variable
            createGrid(meadowLayer.fields);

            // get a reference the meadowlayerview when it is ready.
            view.whenLayerView(meadowLayer).then(function(layerView) {
              meadowLayerView = layerView;
              popGrid();
            });
          })
          .catch(errorCallback);

      });
  </script>
</head>

<body>
  <div id="viewDiv">
    <div id="info">
      <span class="info">
        <b>Populating grid...</b>
      </span>
      <br />
    </div>
    <div id="gridDisplay">
      <span class="info" id="featureCount"></span>
      <div id="grid"></div>
    </div>
  </div>
</body>

</html>
Tags (1)
0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Mason,

   You are attempting to populate you grid before the layer is done updating to the map.

        // create a grid with given columns once the layer is loaded
        meadowLayer.when(function () {
            // create a grid with columns specified in gridFields variable
            createGrid(meadowLayer.fields);

            // get a reference the meadowlayerview when it is ready.
            view.whenLayerView(meadowLayer).then(function (layerView) {
              meadowLayerView = layerView;
//wait for the layerview to be done updating
              meadowLayerView.watch("updating", function(bool){
                if(!bool){
                  popGrid();
                }
              })
            });
          })
          .catch(errorCallback);

View solution in original post

4 Replies
RobertScheitlin__GISP
MVP Emeritus

Mason,

   You are attempting to populate you grid before the layer is done updating to the map.

        // create a grid with given columns once the layer is loaded
        meadowLayer.when(function () {
            // create a grid with columns specified in gridFields variable
            createGrid(meadowLayer.fields);

            // get a reference the meadowlayerview when it is ready.
            view.whenLayerView(meadowLayer).then(function (layerView) {
              meadowLayerView = layerView;
//wait for the layerview to be done updating
              meadowLayerView.watch("updating", function(bool){
                if(!bool){
                  popGrid();
                }
              })
            });
          })
          .catch(errorCallback);
MasonBindl
New Contributor II

That did it. Thank you Robert! I'm new to creating apps with the 4.x JS API and JS in general, so your examples and guidance have been invaluable. 

Much appreciated!

After I clean up this example, I need to fold it into the main application by figuring out how to hide the table in an Expand widget and then have it occupy part of the screen when expanded.

Take care,

-Mason

0 Kudos
MasonBindl
New Contributor II

Robert Scheitlin, GISP‌ here's the app I made using your example/help Tahoe SEZ 

I just saw the beta FeatureTable | ArcGIS API for JavaScript 4.15  came out.

What are your thoughts on that? At first glance it looks promising. Is it built on top of DGrid?

Thanks again for the help/example,

Mason

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Nice app. I have not looked into the FeatureTable widget to much yet but yes it looks great.

0 Kudos