8.6 Populating a Dropdown List
Earlier we saw how to provide users with a set of options through a dropdown list (select element). As in that example, it is sometimes easy and appropriate to hard-code the list options into the page's HTML. However, there are also times when the list is quite long or changes over time. In such cases, it makes more sense to populate the list programmatically.
Also earlier in the lesson, we looked at a sample that involved querying earthquakes based on different attribute and spatial criteria. I want to return to that sample now because the well type dropdown list was constructed by identifying the unique values in one of the wells layer’s fields. If you’re going to populate your own lists "on the fly," you’ll want to implement similar logic.
See the Pen Untitled by Jim Detwiler (@jimdetwiler) on CodePen.
First, an empty select element (no child option elements) is defined in the HTML at the bottom of the code.
Within the JS code, a FeatureLayer containing the wells is created on lines 57-65. That layer is added to a new Map and the Map is associated with a new MapView. Then on lines 90-101 comes a chain of promises. The first is associated with the loading of the MapView. Within its callback function, its return object is set to the wells layer. It in turn has a promise defined such that when the layer is finished loading a Query object is created and used in a call to queryFeatures(). Because the Query has no filtering properties set, queryFeatures() returns all features from the wells layer.
Once the query returns its features, they are passed along to a function called getValues(), which is defined just below the promise chain. The getValues() function uses the map() method to iterate through all of the wells features, retrieving the values in the STATUS2 field and producing a new array containing those values. (The values in this array are the same ones you see in the well type dropdown list, though each value is in the array potentially many times.)
The array produced by the map() method in getValues() then gets passed along to the getUniqueValues() function. That function first creates a new empty array that will be used to store each unique value from the STATUS2 field just once. It uses the forEach() method to iterate over all of the values. Within the forEach loop, the idea is to check whether the current value is in the unique value array yet, add it to the array if it’s not, and skip over it if it is.
Looking at the if expression on lines 119-124, it is composed of three smaller expressions:
- uniqueValues.length < 1
- uniqueValues.indexOf(item) === -1
- item !== ""
uniqueValues.length returns the number of items in the array.
uniqueValues.indexOf(item) returns the position of the first occurrence of item in the array. If item is not in the array, the expression will return -1.
The === operator may be new to you. Recall that a single = character is used for assigning values to variables or setting properties. When you want to test for equivalence between two entities, you need to use == or ===. The difference is that === requires a match in not only the values but also the data types, while == requires a match in just the values. For example, consider the following variables:
x = 0; y = false;
And note how the following expressions evaluate:
if (x == y) { // this evaluates to true if (x === y) { // this evaluates to false
So, the indexOf() condition in this example is written with extra caution to ensure the value being examined is truly not yet in the array.
The first two of these expressions are actually evaluated together (note the placement of parentheses) such that if the unique value array is empty or the item is not yet in the array, then the first part of the if condition should evaluate to true.
The last of the three expressions is then examined. It checks to make sure item is not an empty string (i.e., that the STATUS2 value wasn’t blank for the current record).
The use of the && operator means that the first part of the if condition:
uniqueValues.length < 1 || uniqueValues.indexOf(item) === -1
and the second part:
item !== ""
must both be true.
Given the setup of this loop, each value from the STATUS2 field will be added to the uniqueValues array exactly once.
That array is then passed along to the addToSelect() function. That function first uses the array sort() method on the values to put them in alphabetical order, then iterates through them using another forEach loop. In this loop, an option element is created using the createElement() method we saw earlier in the lesson, the text of the element is set to the current iteration value, and the option is added to the select element. Once all of the values have been processed by the loop, the list is fully populated.