Efficient method for preserving dependent dropdown selections with Select2 and Ajax

I'm currently working on a script that dynamically populates the state dropdown menu based on the selected country id. Everything seems to be functioning correctly, but I've encountered an issue where I can only save the country selection using html localStorage upon page reload, and not the state option.

Below is the code snippet in question:

$(document).ready(function() {
  var country_id = null;
  var state_id = null;

  $('#country').select2();
  $('#state').select2();
  $('#city').select2();

  $('select[name="country"]').on('change', function() {
    var country_id = $(this).val();
    if (country_id) {
      $.ajax({
        url: "/world/getStates.php",
        type: "GET",
        data: {
          'country_id': country_id
        },
        dataType: "json",
        success: function(data) {
          $('select[name="state"]').empty();
          $('select[name="city"]').empty();
          $('select[name="state"]').append('<option value="">Select State</option>');
          $.each(JSON.parse(data), function(key, value) {
            $('select[name="state"]').append('<option value="' + value.id + '">' + value.name + '</option>');
          });
        }
      });
    } else {
      $('select[name="state"]').empty();
    }
  });

  // More code for handling state selection...

  $('#country').val("value from localStorage").trigger('change');
  $('#state').val("value from localStorage").trigger('change');
});
// Additional styling and scripts here...

After implementing this logic, I noticed that the country selection is successfully retrieved from localStorage and triggers the change event, but the same process does not apply to the state selection. Any insights on what might be causing this discrepancy?

Answer №1

After the ajax call is successful, the options in the state dropdown are loaded. If your code fires the .val() before the options are loaded, the selected value will not be marked inside the state dropdown. To solve this issue, you should move that part of the code inside the success function of the ajax call and then trigger the change event after the options have been appended to the state dropdown.

Here is a Demo Code:

$(document).ready(function() {

  var country_id = 1 //localStorage.getItem("select2CountryValue");
  var state_id = 3 //localStorage.getItem("select2StateValue");
  var page_load = true; //added this 
  var data = [{
    "id": 1,
    "name": "xyz_State1"
  }, {
    "id": 2,
    "name": "xyz_State2"
  }, {
    "id": 3,
    "name": "xyz_State3"
  }] //this is just the demo datas
  $('#country').select2();
  $('#state').select2();

  $('select[name="country"]').on('change', function() {
    var country_id = $(this).val();
    
    if (country_id) {
      $('select[name="state"]').empty();
      $('select[name="state"]').append('<option value="">Select State</option>');
      $.each(data, function(key, value) {
        $('select[name="state"]').append('<option value="' + value.id + '">' + value.name + '</option>');
      });
      
      if (page_load == true) {
        $('#state').val(state_id).trigger('change'); 
        page_load = false; 
      }
      
    } else {
      $('select[name="state"]').empty();
    }
  });

  $('#country').val(country_id).trigger('change');

});
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" />
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.full.min.js"></script>

<p>
  <span>Country</span>
  <select class="csc-select" name="country" id="country">
    <option value="">Select Country</option>
    <option value="1">
      xyz
    </option>
    <option value="2">
      xyz2
    </option>
  </select>
</p>
<p>
  <span>State</span>
  <select class="csc-select" name="state" id="state">
    <option value="">Select State</option>
  </select>
</p>

Answer №2

Once the change event is triggered for the country selection, the selected country is based on the localStorage value and the change is triggered successfully. However, when the change event is immediately triggered for the state selection right after that, it does not work as expected. What could be causing this issue?

The reason why triggering the change event for your #state list immediately after the country selection event fails is because at that moment, the state list is still empty. Consequently, there are no values to select, rendering the change event ineffective.

The recommended approach is to wait until the state list is populated with options before triggering the change event.

// To work immediately where countries are present in the HTML
$('#country').val("value from localStorage").trigger('change');

// For the state selection, trigger the change event within the success callback of the country Ajax request
$('#state').val("value from localStorage").trigger('change');

An alternative method involves creating a temporary option first:

$('#state').append("temporary <option> created from localStorage");
$('#state').val("value from localStorage").trigger('change');

This workaround eliminates the need to wait for the state list to populate before triggering the change event.


On another note, Select2 provides support for remote data retrieval, eliminating the manual handling of Ajax requests or option creation.

$("#country").select2({
  ajax: {
    url: "/world/getCountries.php"
  },
  placeholder: 'Pick a country',
  minimumInputLength: 1
}).change(function () {
  $("#state").val("").trigger("change");
});

$("#state").select2({
  ajax: {
    url: "/world/getStates.php",
    data: (params) => {
      // include selected country ID in URL parameters 
      params.country_id = $("#country").val();
      return params;
    }
  },
  placeholder: 'Pick a state',
  minimumInputLength: 1
});

// initialize selection using previous data...
$("#country").append('<option value="5">Switzerland</option>');
$("#country").val("5");
$("#state").append('<option value="9">Appenzell</option>');
$("#state").val("9");


// server side mock-up -------------------------------------------------
const countries = [
  {id: 1, text: 'Australia'},
  {id: 2, text: 'Denmark' },
  {id: 3, text: 'Japan'},
  {id: 4, text: 'Norway'},
  {id: 5, text: 'Switzerland'}
];
const states = [
  {id: 1, text: 'New South Wales', country_id: 1},
  {id: 2, text: 'Victoria', country_id: 1},
  {id: 3, text: 'Hovedstaden', country_id: 2},
  {id: 4, text: 'Midtjylland', country_id: 2},
  {id: 5, text: 'Hokkaido', country_id: 3},
  {id: 6, text: 'Shikoku', country_id: 3},
  {id: 7, text: 'Northern Norway', country_id: 4},
  {id: 8, text: 'Southern Norway', country_id: 4},
  {id: 9, text: 'Appenzell', country_id: 5},
  {id: 10, text: 'Zürich', country_id: 5},
];

$.mockjaxSettings.logging = 1;
$.mockjax({
  url: "/world/getCountries.php",
  contentType: "application/json",
  response: function(settings) {
    this.responseText = {
      results: countries.filter(item =>
        !settings.data.term || item.text.toLowerCase().includes(settings.data.term.toLowerCase())
      )
    };
  }
});
$.mockjax({
  url: "/world/getStates.php",
  contentType: "application/json",
  response: function(settings) {
    console.log(settings.data);
    this.responseText = {
      results: states.filter(item =>
        item.country_id == settings.data.country_id && (
          !settings.data.term || item.text.toLowerCase().includes(settings.data.term.toLowerCase())
        )
      )
    };
  }
});
select {
  width: 200px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-mockjax/2.6.0/jquery.mockjax.min.js"></script>

<select id="country"></select>
<select id="state"></select>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to define a shared function for enums in TypeScript?

I have created an enumeration called VideoCategoryEnum: enum VideoCategoryEnum { knowledge = 0, condition = 1, interview = 2, speech = 3, entertainment = 4, news = 5, advertisement = 6, others = 7, } I am looking to implement a shared met ...

Comparing values inputted from JavaScript using PHP

I am passing values from PHP to a script. <img src=\"images/add.jpg\" onclick='add_program_user(".$value['id_program'].",".$value['min_age'].",".$value['max_age'].")' onmouseover=\"this.style.curso ...

Modifying icons with JavaScript

When I click on the play icon, it changes to the pause icon. However, I am only able to change the first icon from play to pause in my JavaScript code. How can I apply this functionality to all the audio elements on the page? let playIcon = "https://ima ...

Tips for eliminating default classes from Mui Typography styling

I’ve been working on creating a Typography element and noticed some default MUI CSS classes added when debugging in the browser. I attempted to disable them by using empty arrays at the end of the sx prop, but it did not work as expected. In the image p ...

I need help figuring out how to use the post method in AJAX to send a username and password to a PHP file and then retrieve the fields. Can someone

Is there a way to access these variables in PHP? I am attempting to retrieve the username and password in PHP files, however, it keeps showing an error message saying 'Undefined index username' when I use $_POST['Username'] <script ...

Transforming an array of HTTP Observables into an Observable that can be piped asynchronously

I am attempting to execute a series of XHR GET requests based on the results of an initial request. I have an array of observables representing the secondary requests I wish to make, and I am able to utilize Array.map for iterating over them and subscribin ...

Handling redirection on a single-page website without javascript functionality turned on

Currently, I am in the process of creating a single-page website using html, css, javascript, and php. My aim is to use jQuery for navigating through the website - allowing the content to fade out when a user clicks on a link in the navigation menu and hav ...

Storing information through an Ajax form in Laravel

I'm currently facing a challenge with saving data from a form into a database using Ajax. The form validation works perfectly when the input fields are empty. However, I'm struggling with the actual process of saving the form data into the databa ...

Ensure that the React Material UI Textfield with the type "number" is validated to have exactly 10 characters

<TextField variant="outlined" required fullWidth id="accno" label="Main Account Number" type="number" name="accno" //inputProps={{ className:"input-acc", pattern: "^.{0,10}$"}} autoComplete="accno" onChange={(e) = ...

Can a modal be triggered using ajax?

$.ajax({ type : 'POST', data : "", url : '<?php echo site_url("adduser/register_user");?>', success : function(data){ $('#error').modal('show'); } }); I ...

Experimenting with TypeScript code using namespaces through jest (ts-jest) testing framework

Whenever I attempt to test TypeScript code: namespace MainNamespace { export class MainClass { public sum(a: number, b: number) : number { return a + b; } } } The test scenario is as follows: describe("main test", () ...

Having Trouble Assigning Three JS Material to Mesh Object

This is the code snippet for creating a glass material: const glassMaterial = new THREE.MeshPhysicalMaterial( { // color: 0xffffff, metalness: 0.25, roughness: 0, transmission: 1.0 color: 0xffffff, metalness: 0.25, roughness: 0, transmi ...

How can I deactivate the main color of the FormLabel when the focus is on the radio button group?

Is there a way to change the color of FormLabel to black instead of the primary color when the radio button group is focused? https://i.sstatic.net/h3hML.png const styles = { formLabel: { color: "#000" }, formLabelFocused: { color: "#000" ...

Having issues with the basic KnockoutJS binding not functioning as expected

There appears to be an issue with my KnockoutJS script that I'm struggling to pinpoint. Below is the snippet of HTML code: <h2>SendMessage</h2> <div class="form-group" id="messageSender"> <label>Select User Type</l ...

If the element is checked and equal to a specific value, take action

I am trying to hide one of the 3 radio buttons on my page. Although they all have the same class, each button has a different value. I attempted to write code to achieve this, but unfortunately, it is hiding all radio buttons instead of just one. Could s ...

Could this be a limitation of Ajax live search, or is it a problem related to the server?

Recently, I encountered a problem with my website's live search bar. While performing a security check on my site, I discovered that continuously inputting data into the search bar caused my server to crash and go down. I am now seeking precautions an ...

inject the HTML content into the <div> container

Snippet: https://jsfiddle.net/x9ghz1ko/ (Includes notes.) In my HTML file, there are two distinct sections: <section class="desktop_only"> and <section class="mobile_only">. The challenge lies in positioning a JavaScript sc ...

How can Socket.io prevent other pages from receiving all messages?

I apologize for the confusing title, but I am in need of some assistance in clarifying my question. The situation is as follows: I have a website page that is receiving messages from a node server. socket.on('item finished', function(data){ ...

I'm curious as to why a webpage tends to load more quickly when CSS files are loaded before script files. Can anyone shed some

While watching a video, I came across some concepts that were a bit difficult for me to grasp. The video mentions that scripts are 'serialized' and can block subsequent files from loading. According to this explanation, Script 1 would load first ...

What is the definition of a type that has the potential to encompass any subtree within an object through recursive processes?

Consider the data structure below: const data = { animilia: { chordata: { mammalia: { carnivora: { canidae: { canis: 'lupus', vulpes: 'vulpe' } } } } }, ...