Search code examples
arcgis-js-api

How to allow user to use two filter with Javascript for ArcGIS


The Have two filters that will allow a user to chose a major command or grade. However, can someone point me into the right direction on how to actually have them listen to each other. for example, if the user pics a 30ABCT and wants to filter that by the grade of E4. What is the way I need to do that?

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>
      MOS Status - Test
    </title>

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

    <style>
      html,
      body,
      
    #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }

    #grade-filter{
      height: 315px;
      width: 150%;
      visibility: hidden;
      }

    .grade-item {
      width: 100%;
      padding: 12px;
      text-align: center;
      vertical-align: baseline;
      cursor: pointer;
      height: 40px;
      }

    .grade-item:focus {
      background-color: dimgrey;
    }

    .grade-item:hover {
      background-color: dimgrey;
    }
      
    #major-command-filter{
      height: 315px;
      width: 150%;
      visibility: hidden;
      }
    .majorCommand-item {
      width: 100%;
      padding: 12px;
      text-align: center;
      vertical-align: baseline;
      cursor: pointer;
      height: 40px;
     
    }

    .majorCommand-item:focus {
      background-color: dimgrey;
    }

    .majorCommand-item:hover {
      background-color: dimgrey;
    }

      .esri-editor .esri-item-list__scroller {
        max-height: 350px;
      }

      #info {
        padding: 14px;
        border-radius: 5px;
        width: 25%;
      }
    </style>

    <script>
      require([
        "esri/Map",
        "esri/layers/FeatureLayer",
        "esri/form/FormTemplate",
        "esri/widgets/Editor",
        "esri/views/MapView",
        "esri/widgets/Expand",
        "esri/popup/content/TextContent",
      ], function (
        Map,
        FeatureLayer,
        FormTemplate,
        Editor,
        MapView,
        Expand,
        TextContent,
      ) {
        const editThisAction = {
          title: "Edit feature",
          id: "edit-this",
          className: "esri-icon-edit",
        };

        let editor, features, majorCommandView, gradeView;
        // Create a popTemplate
        const template = {
          title: "MOS Vacancy: {mos}",
          content:
            "<ul><li>Grade: <b>{grade}</b></li>" +
            "<li>Unit: <b>{unit}</b></li>" +
            "<li>Major Command: <b>{macom}</b></li>" +
            "<li>City: <b>{city}</b></li><ul>",
          actions: [editThisAction],
        };

        // Create a map from the referenced webmap item id
        const featureLayer = new FeatureLayer({
          url:
            "https://ncguardview.nc.gov/server/rest/services/Hosted/test/FeatureServer/0",
          outFields: ["*"],
          displayField: "mos",
          popupTemplate: template,
          formTemplate: {
            title: "NCNG MOS Vacancy",
            description: "Provide information for MOS Vacancy",
            elements: [
              {
                // Autocasts to new GroupElement
                type: "group",
                label: "Military Occupation Specaility",
                //description: "Field inspector information",
                elements: [
                  {
                    // Autocasts to new FieldElement
                    type: "field",
                    fieldName: "mos",
                    label: "MOS",
                  },
                  {
                    type: "field",
                    fieldName: "series",
                    label: "MOS Branch",
                  },
                  {
                    type: "field",
                    fieldName: "jobtitle",
                    label: "MOS Job Title",
                  },
                  {
                    type: "field",
                    fieldName: "grade",
                    label: "MOS Grade",
                  },
                ],
              }, // end of first group element
              {
                type: "group",
                label: "Unit",
                elements: [
                  {
                    type: "field",
                    fieldName: "unit",
                    label: "Unit",
                  },
                  {
                    type: "field",
                    fieldName: "battalion",
                    label: "Battalion",
                  },
                  {
                    type: "field",
                    fieldName: "macom",
                    label: "Major Command",
                  },
                ],
              }, // End of Second Group
              {
                type: "group",
                label: "Location",
                elements: [
                  {
                    type: "field",
                    fieldName: "address",
                    label: "Unit Address",
                  },
                  {
                    type: "field",
                    fieldName: "city",
                    label: "Unit City",
                  },
                  {
                    type: "field",
                    fieldName: "county",
                    label: "Unit County",
                  },
                ],
              }, // End of Second Group,
              {
                type: "group",
                label: "Status",
                elements:[{
                  type: "field",
                  fieldName: "vacancy",
                  label: "Vacancy Status",
                }]
              }
            ],
          },
        });
         // Create the Map
        const map = new Map({
          basemap: "streets-night-vector",
          layers: [featureLayer]
        });
        //map.add(featureLayer);

        const view = new MapView({
          container: "viewDiv",
          map: map,
          zoom: 6,
          center: [-79, 35],
        });
        ///////////////////////////////////////////////////////////////////
        /*                    Grade Filter                  */
        
        // Doesn't really filter out Grade. The filter still inlcudes 
        // whatever grades are at that location
        const gradeNode = document.querySelectorAll(`.grade-item`);
        const gradeElement = document.getElementById("grade-filter");
        gradeElement.addEventListener('click', filterByGrade); // handle user choice
        // Handle selection for user filter
        function filterByGrade(event) {
          const selectedGrade = event.target.getAttribute("data-grade");
          gradeView.filter = {
            where: "grade = '"+ selectedGrade +"'",
          };
        };
        view.whenLayerView(featureLayer).then(function (layerView) {
          gradeView = layerView;
          // set up UI items
          gradeElement.style.visibility = 'visible';
          const gradeExpand = new Expand({
            view: view,
            content: gradeElement,
            expandIconClass: "esri-icon-filter",
            group: "top-left",
          });
          
          // clear filters
          gradeExpand.watch("expanded", function(){
            if(!gradeExpand.expanded) {
              gradeView.filter = null;
            };
          });
          view.ui.add(gradeExpand, 'top-left');
        });
        
        ///////////////////////////////////////////////////////////////////
        const majComNode = document.querySelectorAll(`.majorCommand-item`);
        const majComElement = document.getElementById("major-command-filter");

        // click event handler for seasons choices
        majComElement.addEventListener("click", filterByCommand);

        // User clicked on Major Commands 30th, 449th, 13th, and etc..
        // set an attribute filter on major command layer view
        function filterByCommand(event) {
          const selectedCommand = event.target.getAttribute("data-command");
          majorCommandView.filter = {
            where: "macom = '" + selectedCommand + "'",
          };
        }

        view.whenLayerView(featureLayer).then(function (layerView) {
          majorCommandView = layerView;

          // set up UI items
          majComElement.style.visibility = "visible";
          const majCommandExpand = new Expand({
            view: view,
            content: majComElement,
            expandIconClass: "esri-icon-filter",
            group: "top-left",
          });
          // clear the filters
          majCommandExpand.watch( "expanded", function() {
            if(!majCommandExpand.expanded) {
              majorCommandView.filter = null;
            }
          });
          view.ui.add(majCommandExpand, 'top-left')
          //////////////////////////////////////////
        /* Stop with Filter and Begin with Editor */
   /////////////////////////////////////////////////////
        view.when(function () {
          view.popup.autoOpenEnabled = true; //disable popups

          // Create the Editor
          let editor = new Editor({
            view: view,
            container: document.createElement("div"),
            layerInfos: [
              {
                layer: featureLayer,
                deleteEnabled: true,
                addEnabled: true,
              },
            ],
          });
          // Add widget to top-right of the view
          //view.ui.add(editor, "bottom-right");

          // Execute each time the "Edit Feature" action is called
          function editThis() {
            // If the EditorViewModel's activeWorkflow is null, make the popup not visible
            if (!editor.viewModel.activeWorkFlow) {
              view.popup.visible =false;

              editor.startUpdateWorkflowAtFeatureEdit(
                view.popup.selectedFeature
              );

              view.ui.add(editor, "bottom-right");
              view.popup.spinnerEnabled = true;
            }

            // We need to set a timeout to ensure the editor widget is fully rendered. We
            // then grab it from the DOM stack
            setTimeout(function () {
              // Use the editor's back button as a way to cancel out of editing
              let arrComp = editor.domNode.getElementsByClassName(
                "esri-editor__back-button esri-interactive"
              );
              if (arrComp.length === 1) {
                // Add a tooltip for the back button
                arrComp[0].setAttribute(
                  "title",
                  "Cancel edits, return to popup"
                );
                // Add a listerner to listen for when the editor's back button is clicked
                arrComp[0].addEventListener("click", function (evt) {
                  console.log(evt);
                  // Prevent the default behavior for the back button and instead remove the editor and reopen the popup
                  evt.preventDefault();
                  //view.ui.remove(editor);
                  view.popup.open({
                    features: features,
                  });
                });
              }
            }, 150);
          }
          // Event handler that fires each time an action is clicked
          view.popup.on("trigger-action", function (event) {
            if (event.action.id === "edit-this") {
              editThis();
            }
          });

          // Watch when the popup is visible
          view.popup.watch("visible", function (event) {
            // Check the Editor's viewModel state, if it is currently open and editing existing features, disable popups
            if (editor.viewModel.state === "editing-existing-feature") {
              view.popup.close();
            } else {
              // Grab the features of the popup
              features = view.popup.features;
            }
          });
          featureLayer.on("apply-edits", function () {
            // Once edits are applied to the layer, remove the Editor from the UI
            //view.ui.remove(editor);

            // Iterate through the features
            features.forEach(function (feature) {
              // Reset the template for the feature if it was edited
              feature.popupTemplate = template;
            });

            // Open the popup again and reset its content after updates were made on the feature
            if (features) {
              view.popup.open({
                features: features,
              });
            }

            // Cancel the workflow so that once edits are applied, a new popup can be displayed
            editor.viewModel.cancelWorkflow();
          });
        });
        });
        view.ui.add("info", {
          position: "bottom-left",
          index: 1,
        });
      });
    </script>
  </head>

  <body>
    <div id="viewDiv"></div>
    <div id="major-command-filter" class="esri-widget">
      <div class="majorCommand-item visible-command" data-command="30 ABCT">30 ABCT</div>
      <div class="majorCommand-item visible-command" data-command="130 MEB">130 MEB</div>
      <div class="majorCommand-item visible-command" data-command="113 SB">113 SB</div>
      <div class="majorCommand-item visible-command" data-command="60 TC">60 TC</div>
      <div class="majorCommand-item visible-command" data-command="449 TAB">449 TAB</div>
      <div class="majorCommand-item visible-command" data-command="139 REG">139 REG</div>
      <div class="majorCommand-item visible-command" data-command="JFHQ">JFHQ</div>
      <div class="majorCommand-item visible-command" data-command="TEST">TEST</div>
    </dvi>
    <div id="grade-filter" class="esri-widget">
        <div class="grade-item visible-grade" data-grade="E1">E1</div>
        <div class="grade-item visible-grade" data-grade="E2">E2</div>
        <div class="grade-item visible-grade" data-grade="E3">E3</div>
        <div class="grade-item visible-grade" data-grade="E4">E4</div>
        <div class="grade-item visible-grade" data-grade="E5">E5</div>
        <div class="grade-item visible-grade" data-grade="E6">E6</div>
        <div class="grade-item visible-grade" data-grade="E7">E7</div>
        <div class="grade-item visible-grade" data-grade="E8">E8</div>
    </dvi>
    <div id="info" class="esri-widget">
      <h3>Select a feature to display its popup</h3>
      <h4>
        Edit the feature by clicking on the "Edit feature" action within the
        popup.
      </h4>
    </div>
  </body>
</html>

Solution

  • You had the right idea, just a couple of things:

    • you just needed an "AND" condition to mix major command and grade
    • if you want to group the expand then you will need to use another mechanism to reset the filters, like a reset button or similar. This because only one expand will be open in a group.

    Take a look at the code I made for you, base on your code but simplify to just the filter logic.

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8" />
      <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
      <title>
        MOS Status - Test
      </title>
    
      <link rel="stylesheet" href="https://js.arcgis.com/4.18/esri/themes/light/main.css" />
      <script src="https://js.arcgis.com/4.18/"></script>
    
      <style>
        html,
        body,
    
        #viewDiv {
          padding: 0;
          margin: 0;
          height: 100%;
          width: 100%;
        }
    
        #grade-filter {
          height: 315px;
          width: 150%;
        }
    
        .grade-item {
          width: 100%;
          padding: 12px;
          text-align: center;
          vertical-align: baseline;
          cursor: pointer;
          height: 40px;
        }
    
        .grade-item:focus {
          background-color: dimgrey;
        }
    
        .grade-item:hover {
          background-color: dimgrey;
        }
    
        #major-command-filter {
          height: 315px;
          width: 150%;
        }
    
        .majorCommand-item {
          width: 100%;
          padding: 12px;
          text-align: center;
          vertical-align: baseline;
          cursor: pointer;
          height: 40px;
    
        }
    
        .majorCommand-item:focus {
          background-color: dimgrey;
        }
    
        .majorCommand-item:hover {
          background-color: dimgrey;
        }
      </style>
    
      <script>
        require([
          "esri/Map",
          "esri/layers/FeatureLayer",
          "esri/views/MapView",
          "esri/widgets/Expand"
        ], function (
          Map,
          FeatureLayer,
          MapView,
          Expand
        ) {
    
          // map & layer set up
    
          const featureLayer = new FeatureLayer({
            url: "https://ncguardview.nc.gov/server/rest/services/Hosted/test/FeatureServer/0",
            outFields: ["*"]
          });
          const map = new Map({
            basemap: "streets-night-vector",
            layers: [featureLayer]
          });
          const view = new MapView({
            container: "viewDiv",
            map: map,
            zoom: 6,
            center: [-79, 35],
          });
          let flView = null;
          view.whenLayerView(featureLayer).then(layerView => flView = layerView);
          
          // filter logic
    
          let selectedGrade = null;
          let selectedCommand = null;
          const updateFilter = function() {
            let conditions = [];
            if (selectedGrade) {
              conditions.push(`(grade='${selectedGrade}')`);
            }
            if (selectedCommand) {
              conditions.push(`(macom='${selectedCommand}')`);
            }
            flView.filter = conditions.length > 0 ? {where: conditions.join("AND")} : null;
            console.log(flView.filter && flView.filter.where);
          }
    
          // click event handlers
    
          const filterByGrade = (event) => {
            selectedGrade = event.target.getAttribute("data-grade");
            updateFilter();
          }
          const filterByCommand = (event) => {
            selectedCommand = event.target.getAttribute("data-command");
            updateFilter();
          }
    
          // grade ui set up
    
          const gradeElement = document.getElementById("grade-filter");
          const gradeExpand = new Expand({
            view: view,
            content: gradeElement,
            expandIconClass: "esri-icon-filter",
            // group: "top-left",
          });
          gradeExpand.watch("expanded", expanded => {
            if (!expanded) {
              selectedGrade = null;
              updateFilter();
            }
          });
          view.ui.add(gradeExpand, 'top-left');
    
          // major command ui set up
    
          const majComElement = document.getElementById("major-command-filter");
          const majCommandExpand = new Expand({
            view: view,
            content: majComElement,
            expandIconClass: "esri-icon-filter",
            // group: "top-left",
          });
          majCommandExpand.watch("expanded", expanded => {
            if (!expanded) {
              selectedCommand = null;
              updateFilter();
            }
          });
          view.ui.add(majCommandExpand, 'top-left');
    
          // listen click events
    
          gradeElement.addEventListener('click', filterByGrade);
          majComElement.addEventListener("click", filterByCommand);
          
        });
      </script>
    </head>
    
    <body>
      <div id="viewDiv"></div>
      <div id="major-command-filter" class="esri-widget">
        <div class="majorCommand-item visible-command" data-command="30 ABCT">30 ABCT</div>
        <div class="majorCommand-item visible-command" data-command="130 MEB">130 MEB</div>
        <div class="majorCommand-item visible-command" data-command="113 SB">113 SB</div>
        <div class="majorCommand-item visible-command" data-command="60 TC">60 TC</div>
        <div class="majorCommand-item visible-command" data-command="449 TAB">449 TAB</div>
        <div class="majorCommand-item visible-command" data-command="139 REG">139 REG</div>
        <div class="majorCommand-item visible-command" data-command="JFHQ">JFHQ</div>
        <div class="majorCommand-item visible-command" data-command="TEST">TEST</div>
      </div>
      <div id="grade-filter" class="esri-widget">
        <div class="grade-item visible-grade" data-grade="E1">E1</div>
        <div class="grade-item visible-grade" data-grade="E2">E2</div>
        <div class="grade-item visible-grade" data-grade="E3">E3</div>
        <div class="grade-item visible-grade" data-grade="E4">E4</div>
        <div class="grade-item visible-grade" data-grade="E5">E5</div>
        <div class="grade-item visible-grade" data-grade="E6">E6</div>
        <div class="grade-item visible-grade" data-grade="E7">E7</div>
        <div class="grade-item visible-grade" data-grade="E8">E8</div>
      </div>
    </body>
    
    </html>