3

I have 3 dropdown, the values filled dynamically with mysql. Now, I'm trying to cascade the 3 dropdown, but JS script is not working.

What I'm trying to do's:

Case 1: If the user choose a value from dropdown #1, the value of dropdown #2 depends on dropdown #1, the value of dropdown #3 depends on dropdown #2. E.g. State - City - Avenue

Case 2: The user can choose from 3 dropdown.

Case 3 and 4: If the user choose from dropdown #2, the values of dropdown #3 depends in dropdown #2 (but choosing a value in dropdown #3 is optional, if the dropdown #2 have already)

Form:

<form action='' method='post' id='loc'>
<select name="state" id="filter_region" class="state">
    <option name="default" class="default" value="State" readonly>State</option>
    <?php
    foreach($result_state as $option){
        if(isset($_POST['state']) && $_POST['state'] == $option->state)
            echo '<option name="state" class="filter_by" selected value="'. $option->state .'">'. $option->state .'</option>';
        else    
         echo '<option name="state" class="filter_by" value="'. $option->state .'">'. $option->state .'</option>';
     };
    ?>
</select>

<select name="city" id="filter_city" class="city">
    <option name="default" class="default" value="City" readonly>City</option>
    <?php
    foreach($result_city as $option){
        if(isset($_POST['city']) && $_POST['city'] == $option->city)
            echo '<option name="city" class="filter_by" selected value="'. $option->city .'">'. $option->city .'</option>';
        else    
         echo '<option name="city" class="filter_by" value="'. $option->city .'">'. $option->city .'</option>';
     };
    ?>
</select>
<select name="avenue" id="filter_mall" class="avenue">
    <option name="default" class="default" value="Avenue" readonly>Avenue</option>
    <?php 
    foreach($result_avenue as $option){
        if(isset($_POST['avenue']) && $_POST['avenue'] == $option->avenue)
            echo '<option name="avenue" class="default" selected value="'. $option->avenue .'">'. $option->avenue .'</option>';
        else    
         echo '<option name="avenue" class="filter_by" value="'. $option->avenue .'">'. $option->avenue .'</option>';
     };
    ?>
</select>
<input type="submit" value="submit" class="submit"/>
</form>

JS:

function cascadeSelect(parent, child){
    var childOptions = child.find('option:not(.default)');
    child.data('options',childOptions);

    parent.change(function(){
        childOptions.remove();
        child
            .append(child.data('options').filter('.filter_by' + this.value))
            .change();
    })

    childOptions.not('.default, .filter_by' + parent.val()).remove();
}

$(function(){
    cascadeForm = $('.loc');
    state= cascadeForm.find('.state');
    city= cascadeForm.find('.city');
    avenue= cascadeForm.find('.avenue');

    cascadeSelect(state, city);
    cascadeSelect(city, avenue);
});
User014019
  • 1,215
  • 8
  • 34
  • 66

1 Answers1

4

I had created cascading parent-child select-boxes using AngularJS, but since you are using NativeJS, I tried to re-create using JS. The pre-requisite of my solution is a well-defined JSON based on which the select-boxes will be created. You'll have to create the JSON on server side or manually or wherever you are creating the select-box data. Below is the JSON format.:

Every select-box is a named object with following properties:

  1. Parent Attribute: Name of object which is the parent of this select-box object.
  2. Options: Array of option objects, where each object contains: (a) Option Value (b) Parent Option Value - The parent select-box value with which the current value is mapped. (c) Option ID.

  3. Selected Option: An object with two properties: (a) Currently selected value (b) ID of currently selected value.

enter image description here

Here is the working Plunker of the solution. Hope it helps.

----- EDIT: -----

On request of @User014019

Here is the another version of this drop-down solution where all the options in all select-boxes are visible initially, and parent-child relationships are set when user select a particular value.


Below is the code:

// reads the data and creates the DOM elements (select-boxes and their relevant options)
function initSelect(data) {
  var select, option, input, filteredOptions;
  for (var key in data) {
    select = document.createElement("select");
    select.name = key;
    container.appendChild(select);
    filteredOptions = optionFilter(data[key].availableOptions, data[data[key].parent], data[key]);
    input = document.querySelector('select[name="' + key + '"');
    input.setAttribute("onchange", "updateSelect(this)");
    for (var i = 0; i < filteredOptions.length; i++) {
      option = document.createElement("option");
      option.value = filteredOptions[i].value;
      option.innerHTML = filteredOptions[i].value;
      input.appendChild(option);
    }
    input.options.selectedIndex = getSelectedIndex(filteredOptions, data[key]);
    input.setAttribute('model', data[key].selectedOption.value);
  }
}


// this function will be called on change of select-box
function updateSelect(element) {
  var input, option;
  setSelectedOption(element);
  for (var key in data) {
    filteredOptions = optionFilter(data[key].availableOptions, data[data[key].parent], data[key]);
    input = document.querySelector('select[name="' + key + '"');
    while (input.firstChild) {
      input.removeChild(input.firstChild);
    }
    for (var i = 0; i < filteredOptions.length; i++) {
      option = document.createElement("option");
      option.value = filteredOptions[i].value;
      option.innerHTML = filteredOptions[i].value;
      input.appendChild(option);
    }
    input.options.selectedIndex = getSelectedIndex(filteredOptions, data[key]);
    input.setAttribute('model', data[key].selectedOption.value);
  }
}

// set the selected-option of select-box when it's changed
function setSelectedOption(element) {
  var inputName = element.getAttribute("name");
  var inputValue = getSelectedText(element);
  var inputItem = data[inputName];
  var selectedOption, filteredOptions;

  // setting selected option of changed select-box
  for (var i = 0; i < inputItem.availableOptions.length; i++) {
    if (inputValue === inputItem.availableOptions[i].value) {
      inputItem.selectedOption = inputItem.availableOptions[i];
      break;
    }
  }
  // setting child object selected option now
  for (var key in data) {
    if (data[key].parent === inputName) {
      filteredOptions = optionFilter(data[key].availableOptions, data[data[key].parent], data[key]);
      data[key].selectedOption = filteredOptions[0];
    }
  }
}

// get the text of select-box
function getSelectedText(element) {
  if (element.selectedIndex == -1) {
    return null;
  }
  return element.options[element.selectedIndex].text;
}

function getSelectedIndex(options, self) {
  var index;
  for (var i = 0; i < options.length; i++) {
    if (self.selectedOption.value === options[i].value) {
      index = i;
      break;
    }
  }
  return index;
}

// get filtered options based on parent's selected value
function optionFilter(items, parent, self) {
  var result = [];
  if (typeof parent !== "undefined") {
    for (var i = 0; i < items.length; i++) {
      if (typeof parent.selectedOption !== "undefined") {
        if (parent.selectedOption !== null && items[i].parentValue === parent.selectedOption.value) {
          result.push(items[i]);
        }
      }
    }
    if (typeof self.selectedOption === "undefined") {
      self.selectedOption = null;
    }
    if (self.selectedOption === null) {
      self.selectedOption = result[0];
    }
    return result;
  } else {
    return items;
  }
}
Community
  • 1
  • 1
Rishabh
  • 900
  • 10
  • 30
  • how can i use my existing template of dropdown with your js? – User014019 Aug 23 '16 at 02:34
  • how to use the data from mysql? – User014019 Aug 23 '16 at 03:13
  • The solution I posted is completely dependent on the data-structure I have used (explained in post). And the HTML generation is completely dynamic (no manual or static HTML code). You'll have to re-structure your complete code if you wish to use this solution. As for MySql, can you please elaborate you question a little bit more. – Rishabh Aug 23 '16 at 06:00
  • The data of each dropdown is fulfilled from mysql dbase. How can I call the data in `function getData()` if from mysql dbase? – User014019 Aug 23 '16 at 06:07
  • How are you getting the MySql data in client-side currently? Are you using any middle layer? – Rishabh Aug 23 '16 at 06:11
  • I created a php script with json_encode and call that php file with XMLHttpRequest – User014019 Aug 23 '16 at 06:47
  • Don't know anything about PHP :) but you can create the JSON in required format by getting the data from MySql and parsing it using PHP if possible. You can post a separate question on this topic if you face problems. – Rishabh Aug 23 '16 at 06:57
  • Ok. Thank you for your help! :) – User014019 Aug 23 '16 at 07:37
  • what do call the data inside `function getData(){}`? It is JSON? – User014019 Aug 26 '16 at 10:05
  • Yes, it's basically a JavaScript object which can be converted to JSON using JSON.stringify() method. You can also convert a string of JSON text to JavaScript object by using JSON.parse(). See this answer: http://stackoverflow.com/a/17785623/3152857 – Rishabh Aug 26 '16 at 10:10
  • So it would like this, json.stringify(json.parse(data))? Since it `return data`. So if I converted it to JSON. How can I use it in JS functions of cascacading? – User014019 Aug 26 '16 at 10:16
  • You can do `return JSON.parse(data);` – Rishabh Aug 26 '16 at 10:36
  • So `return JSON.parse(data);` after the close bracket of `function getData()`? – User014019 Aug 26 '16 at 10:44
  • Yes. `return JSON.parse(data);` would return the `data` as JavaScript object after parsing the JSON string. You can get the `data` from server or create locally. But if you are creating the `data` locally (as a JavaScript object, as it's currently coded in plunker), you don't need `JSON.parse()` since it's not JSON but a plain JavaScript object. – Rishabh Aug 26 '16 at 10:48
  • See. If I created a JSON from Mysql and PHP and parse it to JS, I will use `return JSON.parse(data);`, right? And how to inline the dropdowns? – User014019 Aug 30 '16 at 00:08
  • All values for each drop-down are not supposed to be there if there is a parent-child relationship between drop-downs and their values. If you want all values, then you don't need to put all this code to create parent-child drop-downs. – Rishabh Aug 31 '16 at 09:26
  • what I need is the user can choose in three dropdown. The user can select in 2nd dropdown or 3rd dropdown without select any value in 1st dropdown – User014019 Aug 31 '16 at 09:39
  • So just change the parent-child relationship in the JSON. See this plunker (I updated State object's parent and parent values as null): https://plnkr.co/edit/SxByDkrjHUZEkkcUWybj?p=preview – Rishabh Aug 31 '16 at 10:04
  • The sample is not working. What i mean is, if the user choose a value from 1st dropdown - the values in 2nd dropdown will change depends on the value of 1st - the values of 3rd dropdown will change depends on the value of 2nd. But the values of 2nd and 3rd dropdown will displayed all, it only change if the user choose a value from 1st dropdown – User014019 Aug 31 '16 at 10:26
  • It's not possible in the current implementation, because the parent-child relationships are set at the beginning. The code will only render the options according the the relationships (which are already set). And this is completely consistent. The functionality you require is not consistent with the functionality of parent-child select-boxes, because anyways if you change the "parent" select-box, filtered options will be shown in child. So why display all options only the "first time"? Anyways you are free to modify the code according to your needs. – Rishabh Aug 31 '16 at 10:54