ArcGIS API for JS 4.15: queryTask within collapsable div

103
2
Jump to solution
a month ago
Highlighted
New Contributor

Today in trying to learn JavaScript: I've created a div element inside of the expand widget. The div element holds a drop-down list and a search button. The search button activates a query task.

The query function works when the div is outside of the expand widget (ie, if I add the div and associated html straight to the view), but not inside of it. Currently, trying to query the featurelayer results in the error: "Cannot read property 'value' of null."

I'm not sure how to correctly point the queryTask to the div inside var node. I've tried a few different approaches involved with changing the "document" call in places like "document.getElementById", but nothing has worked so far.

Complete code below:

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

<title>Project viewer</title>

<style>

html,
body,
#mainViewDiv {
padding: 0;
margin: 0;
height: 100%;
overflow: hidden;
}

p {
color: black;
font-family: "Noto Sans", sans-serif;
line-height: 2;
margin: 0;
padding: 0;
}

#results {
padding-top: 5px;
}

#optionsDiv {
background-color: white;
color: white;
padding: 10px;
width: 100%;
-webkit-box-shadow: 3px 2px 15px -2px rgba(0,0,0,0.67);
-moz-box-shadow: 3px 2px 15px -2px rgba(0,0,0,0.67);
box-shadow: 3px 2px 15px -2px rgba(0,0,0,0.67);
opacity: 0.90;
}

.myPanel {
}

#drop-downs {
padding-bottom: 15px;
}

#printResults {
font-family: "Noto Sans", sans-serif;
font-style: italic;
font-size: 12px;
color: black;
}

#doBtn {
box-shadow: inset 0px 0px 0px 0px #9fb4f2;
background-color: #1a1b1f;
border: 0px solid #4e6096;
display: inline-block;
cursor: pointer;
color: #ffffff;
padding: 6px 12px;
text-decoration: none;
border: 0px;
outline: none;
transition: all 0.3s ease 0s;
}
#doBtn:hover {
background-color: #494b51;
outline: none;
}
#doBtn:active {
position: relative;
top: 1px;
border: 0px;
outline: none;
}

.widget {
border-radius:4px;
border:1px solid #AAAAAA;
}

</style>

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

<script>
require([
"esri/Map",
"esri/views/MapView",
"dojo/dom-construct",
"dojo/dom-class",
"esri/core/watchUtils",
"esri/Basemap",
"esri/widgets/LayerList",
"esri/layers/FeatureLayer",
"esri/PopupTemplate",
"esri/layers/GraphicsLayer",
"esri/tasks/QueryTask",
"esri/tasks/support/Query",
"esri/widgets/Expand",
"dojo/domReady!",
"dojo/dom",
"dojo/on"
], function(
Map,
MapView,
domConstruct,
domClass,
watchUtils,
Basemap,
LayerList,
FeatureLayer,
PopupTemplate,
GraphicsLayer,
QueryTask,
Query,
Expand,
dom,
on
){

var basinUrl = "https://services.arcgis.com/v01gqwM5QqNysAAi/arcgis/rest/services/Chesapeake_Bay_major_watersheds_feature/FeatureServer/0";

//* Define the popup content for each result
var popupTemplate = {
title: "{MajBas}",
content:
"Test text"
};

// Layer - project footprints
const basinLayer = new FeatureLayer({
url: basinUrl,
outFields: ["*"],
visible: true,
popupTemplate: popupTemplate,
title: "Basins"
});

// Layer - dark nav basemap
const basemap = Basemap.fromId("streets-night-vector");

//** Point querytask to project boundary URL
var qTask = new QueryTask({
url: basinUrl
});

//** Set the query parameters to always return geometry and all fields.
//** Returning geometry allows us to display results on the map/view
var params = new Query({
returnGeometry: true,
outFields: ["*"]
});

//* GraphicsLayer for displaying results
var resultsLayer = new GraphicsLayer({
listMode: "hide"
});

var map = new Map({
basemap : basemap,
layers: [basinLayer, resultsLayer]
});

var mainView = new MapView({
container: "mainViewDiv",
map: map,
popup: {
highlightEnabled: true,
dockEnabled: true,
dockOptions: {
breakpoint: false,
position: "top-right"
}
},
center: [-75.325395, 40.306275],
zoom: 5
});

// * create query div
var node = domConstruct.create("div", {
idName: "myPanel",
innerHTML: "<div id=\"optionsDiv\"><div id=\"drop-downs\"><p><b>Basin</b></p><select id=\"valSelect\" class=\"widget\"><option value=\"Susquehanna River Basin\">Susquehanna River Basin</option><option value=\"Potomac River Basin\">Potomac River Basin</option><option value=\"James River Basin\">James River Basin</option></select></div><div align=\"center\"><button id=\"doBtn\">Search</button></div><div align=\"center\"><p id=\"results\"><span id=\"printResults\"></span></p></div></div></div>"
});

// * create collapsable query panel
var widgetPanel = new Expand({
view: mainView,
expanded: true,
expandTooltip: "Search basins",
content: node
});

//* add expand widget containing widgetPanel
mainView.ui.add([widgetPanel], "bottom-left");

// for drop down
var basinTypeSelect = document.getElementById("valSelect");

// call doQuery() each time the button is clicked
mainView.when(function () {
mainView.ui.add(widgetPanel, "bottom-left");
document.getElementById("doBtn").addEventListener("click", doQuery);
});

// relate to drop-down menu select field ID
var attributeName = document.getElementById("MajBas");

// executes each time the button is clicked
function doQuery() {
// Clear the results from a previous query
resultsLayer.removeAll();
// Build new query
params.where =
"MajBas LIKE" + "'" + basinTypeSelect.value + "'";

// executes query and calls getResults() once promise is resolved
// promiseRejected() is called if the promise is rejected
qTask.execute(params).then(getResults).catch(promiseRejected);
}

// called each time the promise is resolved
function getResults(response) {
// loop through each results and assign a symbol and PopupTemplate
var basinResults = response.features.map(function (feature) {
// Sets the symbol of each resulting feature
feature.symbol = {
type: "simple-fill",
color: [212, 161, 87, 0.25],
outline: { // autocasts as new SimpleLineSymbol()
color: [128, 128, 128, 0.5],
width: "0.5px"
}
};

feature.popupTemplate = popupTemplate;
return feature;
});

resultsLayer.addMany(basinResults);

// animate to the results after they are added to the map
mainView
.goTo(basinResults)
.then(function () {
mainView.popup.open({
features: basinResults,
featureMenuOpen: true,
updateLocationEnabled: true
});
})
.catch(function (error) {
if (error.name != "AbortError") {
console.error(error);
}
});

// print the number of results returned to the user
document.getElementById("printResults").innerHTML =
basinResults.length + " result(s) found";
}

// Called each time the promise is rejected
function promiseRejected(error) {
console.error("Promise rejected: ", error.message);
}

});

</script>
</head>

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

</body>
</html>
Tags (2)
Reply
0 Kudos
1 Solution

Accepted Solutions
Highlighted
MVP Esteemed Contributor

Kaycee,

  The issue was that the basinTypeSelect was null inside the doQuery function. Here is the fix.

        // for drop down
var basinTypeSelect;

// call doQuery() each time the button is clicked
mainView.when(function () {
mainView.ui.add(widgetPanel, "bottom-left");
document.getElementById("doBtn").addEventListener("click", doQuery);
//Wait for the view to be ready and then the valSelect will be ready too.
basinTypeSelect = document.getElementById("valSelect")
});

// relate to drop-down menu select field ID
var attributeName = document.getElementById("MajBas");

// executes each time the button is clicked
function doQuery() {
// Clear the results from a previous query
resultsLayer.removeAll();
// Build new query
params.where =
"MajBas LIKE" + "'" + basinTypeSelect.value + "'";

// executes query and calls getResults() once promise is resolved
// promiseRejected() is called if the promise is rejected
qTask.execute(params).then(getResults).catch(promiseRejected);
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

View solution in original post

2 Replies
Highlighted
MVP Esteemed Contributor

Kaycee,

  The issue was that the basinTypeSelect was null inside the doQuery function. Here is the fix.

        // for drop down
var basinTypeSelect;

// call doQuery() each time the button is clicked
mainView.when(function () {
mainView.ui.add(widgetPanel, "bottom-left");
document.getElementById("doBtn").addEventListener("click", doQuery);
//Wait for the view to be ready and then the valSelect will be ready too.
basinTypeSelect = document.getElementById("valSelect")
});

// relate to drop-down menu select field ID
var attributeName = document.getElementById("MajBas");

// executes each time the button is clicked
function doQuery() {
// Clear the results from a previous query
resultsLayer.removeAll();
// Build new query
params.where =
"MajBas LIKE" + "'" + basinTypeSelect.value + "'";

// executes query and calls getResults() once promise is resolved
// promiseRejected() is called if the promise is rejected
qTask.execute(params).then(getResults).catch(promiseRejected);
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

View solution in original post

Highlighted
New Contributor

Thank you, Robert! You're awesome. I appreciate you taking the time to pinpoint the issue.

Reply
0 Kudos