Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Making Select2 (4.x) list boxes cascading / dependent. Options of a select2 list box will be loaded/refreshed by ajax based on selection of another select2 list box.
/**
* A Javascript module to loadeding/refreshing options of a select2 list box using ajax based on selection of another select2 list box.
*
* @url : https://gist.github.com/ajaxray/187e7c9a00666a7ffff52a8a69b8bf31
* @auther : Anis Uddin Ahmad <anis.programmer@gmail.com>
*
* Live demo - https://codepen.io/ajaxray/full/oBPbQe/
* w: http://ajaxray.com | t: @ajaxray
*/
var Select2Cascade = ( function(window, $) {
function Select2Cascade(parent, child, url, select2Options) {
var afterActions = [];
var options = select2Options || {};
// Register functions to be called after cascading data loading done
this.then = function(callback) {
afterActions.push(callback);
return this;
};
parent.select2(select2Options).on("change", function (e) {
child.prop("disabled", true);
var _this = this;
$.getJSON(url.replace(':parentId:', $(this).val()), function(items) {
var newOptions = '<option value="">-- Select --</option>';
for(var id in items) {
newOptions += '<option value="'+ id +'">'+ items[id] +'</option>';
}
child.select2('destroy').html(newOptions).prop("disabled", false)
.select2(options);
afterActions.forEach(function (callback) {
callback(parent, child, items);
});
});
});
}
return Select2Cascade;
})( window, $);

Select2 Cascade (for v4.x)

Loadeding/refreshing options of a select2 list box (using ajax) based on selection of another select2 list box.

Check the live demo.

How to use

Create a new instance of Select2Cascade by passing the following 4 things -

  • Parent select2 listbox
  • Child select2 listbox
  • URL to load child items from
  • (OPTIONAL) select2 options

To set the parent selected value in ajax request, keep :parentId: as a placeholder in url. The selected value of parent select2 listbox will replace the :parentId: string in url. For example -
Query string: /path/to/api?type=childType&parent_id=:parentId:
RESTful url: /path/to/api/items/:parentId:/sub-items

The response json should be a flat object of value:label pairs. e,g,

{
  "20150415" : "Chittagong Zila",
  "20190901" : "Comilla Zila",
  "20221601" : "Cox's Bazar Zila",
  "20301401" : "Feni Zila"
}

Otherwisw you have to adjust the way of populating child options (at line 29).

Examples

When #parentList is changed, call to path/to/geocode/district/SELECTED-PARENT/zilla and set the options of #childList from the ajax response.

var options = { width: 'resolve' };
new Select2Cascade($('#district'), $('#zilla'), 'path/to/geocode/district/:parentId:/zilla', options);
new Select2Cascade($('#zilla'), $('#thana'), 'path/to/geocode/zilla/:parentId:/thana', options);

If you want to do something with the response data or selectboxes, you can set (any number of) callbacks to be executed after the child listbox refreshed -

var cascadLoading = new Select2Cascade($('#parent'), $('#child'), 'path/to/api/categories?parent_id=:parentId:');
cascadLoading.then( function(parent, child, items) {
    // Open the child listbox immediately
    child.select2('open');
    // Dump response data
    console.log(items);
})
@m47730
m47730 commented Feb 13, 2017
/**
 * A Javascript module to loadeding/refreshing options of a select2 list box using ajax based on selection of another select2 list box.
 *
 * @url : https://gist.github.com/ajaxray/187e7c9a00666a7ffff52a8a69b8bf31
 * @auther : Anis Uddin Ahmad <anis.programmer@gmail.com>
 *
 * Live demo - https://codepen.io/ajaxray/full/oBPbQe/
 * w: http://ajaxray.com | t: @ajaxray
 */
var Select2Cascade = ( function(window, $) {

    function Select2Cascade(parent, child, url, options) {
        var afterActions = [];

        // Register functions to be called after cascading data loading done
        this.then = function(callback) {
            afterActions.push(callback);
            return this;
        };

        parent.select2(options).on("change", function (e) {

            child.prop("disabled", true);
            var _this = this;

            $.getJSON(url.replace(':parentId:', $(this).val()), function(items) {
                var newOptions = '<option value=""></option>';
                for(var id in items) {
                    newOptions += '<option value="'+ id +'">'+ items[id] +'</option>';
                }

                child.select2('destroy').html(newOptions).prop("disabled", false)
                    .select2(options);

                afterActions.forEach(function (callback) {
                    callback(parent, child, items, options);
                });
            });
        });
    }

    return Select2Cascade;

})( window, $);

a simple modification to support select2 options (as theme)

@ajaxray
Owner
ajaxray commented Feb 21, 2017

@m47730, Very good idea! we can keep the options open for the user.
I am going to add this. Thank you very much 👍

@magicalbanana

@ajaxray how would you set a default value for the child when the value for parent is already selected on load?

@ajaxray
Owner
ajaxray commented Mar 7, 2017 edited

@magicalbanana I guess, as we are re-generating child values from html <option>s, if you keep the attribute selected with an option, select2 will remain it selected by default.

@nicpot
nicpot commented Mar 26, 2017 edited

@ajaxray Thanks for the nice code! I'm using 3 cascaded select2 dropdowns in a bootstrap modal - so I'm re-initializing and destroying the select2 dropdowns in the bootstrap show.bs.modal and hide.bs.modal. Here's what I have:

$("#addressModal").on("hide.bs.modal", function (e) {
    $("#addressProvinceIdId").select2("destroy");
    $("#addressDistrictId").select2("destroy");
    $("#addressMunicipalityId").select2("destroy");
   
    // some other code...
});

$("#addressModal").on("show.bs.modal", function (e) {
    var select2Options = {
        placeholder: "- Select -",
        theme: "bootstrap",
        triggerChange: true,
        allowClear: false,
        dropdownAutoWidth: true,
        minimumResultsForSearch: 50
    };

    $("#addressProvinceId").select2(select2Options);
    $("#addressDistrictId").select2(select2Options);
    $("#addressMunicipalityId").select2(select2Options);

    var provinceCascade = Select2Cascade($("#addressProvinceId"), $("#addressDistrictId"), "/someurl/getdistricts?provinceId=:parentId:", select2Options);
    var districtCascade = Select2Cascade($("#addressDistrictId"), $("#addressMunicipalityId"), "/someurl/getmunicipalities?districtId=:parentId:", select2Options);

    // some other code...
});

I noticed that the Select2Cascade ajax requests would increase as the modal is opened/closed on the same page. So I guess I need to re-init and destroy the Select2Cascade functions as well. What would be the correct way to do this?

@ajaxray
Owner
ajaxray commented Apr 1, 2017

Hello @nicpot,
As we are binding ONLY Select2's change event for handling the association, it it will automatically unbind it on destroy() call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment