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>
Solved! Go to Solution.
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);
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);
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
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
Nice app. I have not looked into the FeatureTable widget to much yet but yes it looks great.