Display attribute table with JavaScript API ver 4.9?

6171
10
Jump to solution
10-21-2018 11:55 PM
lxd
by
New Contributor III

How to display feature layer's attribute table with JavaScript API ver 4.9? 

I am working with ArcGIS API for Javascript 4.9. I want to create a simple map view and display a table under it. I am stuck trying to work out how to display a table displaying data from a feature layer published on ArcGIS  with this version of API. I removed all code related to the map to make it simpler till I work out this table.

Here is my code:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tutorial: Grids and Stores</title>

<link rel="stylesheet" href="https://js.arcgis.com/4.9/esri/css/main.css">
</head>
<body class="claro">
<h1>Display table with feature layer and dgrid </h1>
<div id="grid"></div>


<script src="https://js.arcgis.com/4.9/"></script>
<script>
require([

"dojo/_base/declare",
"dstore/RequestMemory",
"dgrid/Grid",
"dgrid/extensions/Pagination"
], function (declare, RequestMemory, Grid, Pagination) {
// Create a Grid instance using Pagination, referencing the store

var gridFields = ["__OBJECTID", "WAL_NAME", "REGION", "WAL_ZONE", "REGION_WAL", // __OBJECTID
"WAL_Value", "SWAL_Value"
];


var grid = new (declare([ Grid, Pagination ]))({
collection: new RequestMemory({ target: "https://xxxxxxxxx/arcgis/rest/services/GCX/QFESAlertLevels_GCX/FeatureServer/1?f=pjson" }),
className: 'dgrid-autoheight',
columns: gridFields,

}, 'grid');

grid.startup();
});
</script>
</body>
</html>

The error that I get is:

Any ideas? Did anyone get it to work?

Thanks!

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Emeritus

Lidia,

  OK here is the sample re-worked to show all the data.

<!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.9/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/WebMap",
        "esri/views/MapView",
        "esri/layers/CSVLayer",
        "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(
        WebMap, MapView, CSVLayer, Graphic, Legend, Expand, SpatialReference,
        OnDemandGrid, ColumnHider, Memory, StoreAdapter, Selection
      ) {

        let map, view, csvLayer, csvLayerView, grid;
        const gridDiv = document.getElementById("grid");
        const infoDiv = document.getElementById("info");

        // create new map, view and csvlayer
        setupTheView();

        const gridFields = ["__OBJECTID", "Category", "Season", "Name",
          "Nature", "wmo_wind"
        ];

        // 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"
          })
        });

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

            // get a reference the csvlayerview when it is ready. It will used to do
            // client side queries when user draws polygon to select features
            view.whenLayerView(csvLayer).then(function(layerView) {
              csvLayerView = layerView;
              popGrid();
            });
          })
          .catch(errorCallback);

        /****************************************************
         * Selects features from the csv layer that intersect
         * a polygon that user drew using sketch view model
         ****************************************************/
        function popGrid() {
          view.graphics.removeAll();
          if (csvLayerView) {
            const query = {
              where: "1=1",
              outFields: ["*"]
            };

            // query graphics from the csv layer view. Geometry set for the query
            // can be polygon for point features and only intersecting geometries are returned
            csvLayerView.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
          csvLayerView.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 poverty
         * csvLayer's 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);
        }

        /******************************************************
         * Sets up the view. WebMap with winkel III projection
         * basemap and hurricanes CsvLayer is added to the view.
         ******************************************************/
        function setupTheView() {
          const url =
            "https://arcgis.github.io/arcgis-samples-javascript/sample-data/hurricanes.csv";

          csvLayer = new CSVLayer({
            title: "Hurricanes",
            url: url,
            spatialReference: new SpatialReference({wkid: 54042}),
            copyright: "NOAA",
            popupTemplate: {
              title: "{Name}",
              content: [{
                type: "text",
                text: "Category {Category} storm with that occurred at {ISO_time}."
              }, {
                type: "fields",
                fieldInfos: [{
                  fieldName: "wmo_pres",
                  label: "Pressure"
                }, {
                  fieldName: "wmo_wind",
                  label: "Wind Speed (mph)"
                }]
              }],
              fieldInfos: [{
                filedName: "ISO_time",
                format: {
                  dateFormat: "short-date-short-time"
                }
              }]
            },
            renderer: {
              type: "unique-value",
              field: "Category",
              uniqueValueInfos: createUniqueValueInfos()
            }
          });

          map = new WebMap({
            // contains a basemap with a Winkel III projection
            // the CSVLayer coordinates will re-project client-side
            portalItem: {
              id: "7d127cef99a44327b79f5185602b8b6b"
            },
            layers: [csvLayer]
          });

          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");
        }

        /*********************************************************
         * Used to create uniques values for the csvlayer renderer
         *********************************************************/
        function createUniqueValueInfos() {
          const fireflyImages = [
            "cat1.png",
            "cat2.png",
            "cat3.png",
            "cat4.png",
            "cat5.png"
          ];

          const baseUrl =
            "https://arcgis.github.io/arcgis-samples-javascript/sample-data/";

          return fireflyImages.map(function(url, i) {
            return {
              value: i + 1, // Category number
              symbol: {
                type: "picture-marker",
                url: baseUrl + url
              }
            }
          });
        }

        function errorCallback(error) {
          console.log("error:", error)
        }
      });
  </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>

View solution in original post

10 Replies
RobertScheitlin__GISP
MVP Emeritus
0 Kudos
lxd
by
New Contributor III

Hi Robert, yes, I looked into this example. They use selection to set data variable and I don't need to select anything in my case. I am not sure how to replace that bit of the example. 

I have just copied parts of the code to show what I mean.

const graphics = results.features;......

.........

const data = graphics.map(function(feature, i) {......

dataStore.objectStore.data = data;
grid.set("collection", dataStore);

I need data variable to be all features in the dataset... Any ideas on how to achieve it?

Thanks,

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Lidia,

  OK here is the sample re-worked to show all the data.

<!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.9/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/WebMap",
        "esri/views/MapView",
        "esri/layers/CSVLayer",
        "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(
        WebMap, MapView, CSVLayer, Graphic, Legend, Expand, SpatialReference,
        OnDemandGrid, ColumnHider, Memory, StoreAdapter, Selection
      ) {

        let map, view, csvLayer, csvLayerView, grid;
        const gridDiv = document.getElementById("grid");
        const infoDiv = document.getElementById("info");

        // create new map, view and csvlayer
        setupTheView();

        const gridFields = ["__OBJECTID", "Category", "Season", "Name",
          "Nature", "wmo_wind"
        ];

        // 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"
          })
        });

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

            // get a reference the csvlayerview when it is ready. It will used to do
            // client side queries when user draws polygon to select features
            view.whenLayerView(csvLayer).then(function(layerView) {
              csvLayerView = layerView;
              popGrid();
            });
          })
          .catch(errorCallback);

        /****************************************************
         * Selects features from the csv layer that intersect
         * a polygon that user drew using sketch view model
         ****************************************************/
        function popGrid() {
          view.graphics.removeAll();
          if (csvLayerView) {
            const query = {
              where: "1=1",
              outFields: ["*"]
            };

            // query graphics from the csv layer view. Geometry set for the query
            // can be polygon for point features and only intersecting geometries are returned
            csvLayerView.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
          csvLayerView.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 poverty
         * csvLayer's 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);
        }

        /******************************************************
         * Sets up the view. WebMap with winkel III projection
         * basemap and hurricanes CsvLayer is added to the view.
         ******************************************************/
        function setupTheView() {
          const url =
            "https://arcgis.github.io/arcgis-samples-javascript/sample-data/hurricanes.csv";

          csvLayer = new CSVLayer({
            title: "Hurricanes",
            url: url,
            spatialReference: new SpatialReference({wkid: 54042}),
            copyright: "NOAA",
            popupTemplate: {
              title: "{Name}",
              content: [{
                type: "text",
                text: "Category {Category} storm with that occurred at {ISO_time}."
              }, {
                type: "fields",
                fieldInfos: [{
                  fieldName: "wmo_pres",
                  label: "Pressure"
                }, {
                  fieldName: "wmo_wind",
                  label: "Wind Speed (mph)"
                }]
              }],
              fieldInfos: [{
                filedName: "ISO_time",
                format: {
                  dateFormat: "short-date-short-time"
                }
              }]
            },
            renderer: {
              type: "unique-value",
              field: "Category",
              uniqueValueInfos: createUniqueValueInfos()
            }
          });

          map = new WebMap({
            // contains a basemap with a Winkel III projection
            // the CSVLayer coordinates will re-project client-side
            portalItem: {
              id: "7d127cef99a44327b79f5185602b8b6b"
            },
            layers: [csvLayer]
          });

          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");
        }

        /*********************************************************
         * Used to create uniques values for the csvlayer renderer
         *********************************************************/
        function createUniqueValueInfos() {
          const fireflyImages = [
            "cat1.png",
            "cat2.png",
            "cat3.png",
            "cat4.png",
            "cat5.png"
          ];

          const baseUrl =
            "https://arcgis.github.io/arcgis-samples-javascript/sample-data/";

          return fireflyImages.map(function(url, i) {
            return {
              value: i + 1, // Category number
              symbol: {
                type: "picture-marker",
                url: baseUrl + url
              }
            }
          });
        }

        function errorCallback(error) {
          console.log("error:", error)
        }
      });
  </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>
lxd
by
New Contributor III

Hi Robert,

Thank you. Great!

I have redone the code to use with my polygons and added ability to modify one field  by entering values and writing it back to the feature layer.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Glad it worked. Don’t forget to mark this question as answered by clicking the mark correct link on the reply that answered your question.

0 Kudos
KarstenRank
Occasional Contributor III

Hi Robert,

I have added the links to the newest API 4.18 in the solution example:

 

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

 



The problem is, the grid is not filled? Do you know why?

Greetings
Karsten

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Try dropping back to 4.17. I am having issues with grid in 4.18 too.

0 Kudos
KarstenRank
Occasional Contributor III

The same result.

It looks like that it takes to  long for querying for the grid. If I inset a setTimeout the grid get filled:

 

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

            // get a reference the csvlayerview when it is ready. It will used to do
            // client side queries when user draws polygon to select features
            view.whenLayerView(csvLayer).then(function(layerView) {
              csvLayerView = layerView;
              console.log("timeout start");

              setTimeout(function(){ popGrid(); }, 30000);
            });
          })
          .catch(errorCallback);

 

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Karsten,

OK I see what the issue is. The popGrid is getting called before the view has an features in it and because the code is set to query the layer view there are no records to return to the grid. If you change the code to query the actual csv layer and not the layer view then you get the grid with all records of the layer.

        function popGrid() {
          view.graphics.removeAll();
          if (csvLayerView) {
            const query = {
              where: "1=1",
              outFields: ["*"]
            };

            // query graphics from the csv layer view. Geometry set for the query
            // can be polygon for point features and only intersecting geometries are returned
//Changed to csvLayer instead of layer view
            csvLayer.queryFeatures(query).then(function(results) {
...