41

Im trying to build my dropdown menu using the plugin Chosen for Multiple Select . Here's to behavior I'm based on:

http://jsfiddle.net/JfLvA/

So, instead of having 3 harcoded < option > in my select. I want this list to be the values of a json array populated by an ajax request. This will be triggered by autocomplete.

So, if the user type "car", im sending the letter via an ajax call, and im getting back an array like that:

[{"id":"2489","name":"carrie"},{"id":"2490","name":"Caroline"},{"id":"2491","name":"Carole"}]

The code:

$(function() {

$(".chzn-select").chosen();
$(".chzn-select-deselect").chosen({allow_single_deselect:true});

$('.chzn-choices input').autocomplete({
   source: function( request, response ) {
      $.ajax({
          url: "/change/name/autocomplete/"+request.term+"/",
          dataType: "json",
          success: function( data ) {
             response( $.map( data, function( item ) {
                $('ul.chzn-results').append('<li class="active-result">' + item.name + '</li>');

          }
       });
    }
});

Result:

I type "car", in the dropdown Im getting "No result for car" and then I have all my results, as I want.

1. Why I'm I getting the "No result" message, cause I can see in my json array and inside my list that I'm getting results.

 -----------------------------

When I delete "car" and enter "sam". The results for "sam" are showing after the "car" results. (Basically, I see the result for both, instead of just having the result of my current search)

2. Im I suppose to clear the ul on keyUp?? Thought the plugin was doing that already

 -----------------------------

When I click on a name to actually select it and add it into the select, Im getting a javascript error inside the chosen.js file

item is undefined
"item.selected = true;" line 732

the link to the plugin: http://harvesthq.github.com/chosen/chosen/chosen.jquery.js

and it's not adding anything inside the select.

3. No idea why this is happening

 -----------------------------

Do you guys have any idea on what I'm I doing something wrong? I'm completly stuck here...!

Oh and by the way, I dont mind changing the plugin source, as it's the only place where I'm using it....

j0k
  • 22,600
  • 28
  • 79
  • 90
Lelly
  • 960
  • 3
  • 15
  • 29
  • how abt doing $('ul.chzn-results').empty() before append .try it – Ashirvad Aug 20 '12 at 20:29
  • now it's returning me the result for only the term I search, but it's returning only 1 result, even if I have 10 in my array – Lelly Aug 20 '12 at 20:37
  • The solution I found for this was to add $('.ui-autocomplete-input').keyup(function() { $('.chzn-results').empty(); }); But this is just for issue #2 – Lelly Aug 20 '12 at 20:41
  • you shouldn't be manipulating `chzn-choices`. You should append the json items you get from your server to your chzn-select field and then invoke `$(".chzn-select").trigger("liszt:updated");`. –  Dec 02 '12 at 13:57
  • $("#form_field").trigger("chosen:updated"); Used This – Mayur Jul 02 '14 at 10:30
  • Which autocomplete plugin are you using? – c_Reg_c_Lark Oct 18 '19 at 01:31

12 Answers12

44

You can dynamically populate a list via AJAX using the excellent Select2 plugin. From my answer to "Is there a way to dynamically ajax add elements through jquery chosen plugin?":

Take a look at the neat Select2 plugin, which is based on Chosen itself and supports remote data sources (aka AJAX data) and infinite scrolling.

Community
  • 1
  • 1
Vicky Chijwani
  • 10,191
  • 6
  • 56
  • 79
  • 1
    Test select2 before going all in. There are still issues with it. – Karlth Jul 18 '15 at 23:29
  • @Karlth - please can you state some issues you faced, so saves me time and if I have missed any scenarios – Krishna Aug 05 '15 at 08:53
  • 2
    Excellent? Select2 has more than 450 open issues on Github and is basically abandoned, not to mention bloated size.. far from excellent. Also, this is more of a comment and not an answer since it only contains a link which is also not an answer.. – vsync Aug 30 '16 at 08:36
  • @vsync, this answer is over 3 years old, so naturally things have changed. Feel free to downvote. Also, I have quoted my linked answer, so it's not just a link. And linking to a library that solves OP's problem is not against the guidelines, you don't seriously expect people to actually copy-paste the library's source code! – Vicky Chijwani Aug 30 '16 at 08:43
  • 1
    the time passed is irrelevant to the irrelevancy of this answer. the OP asked specifically to help him with the Chosen plugin, and you simply offered another, without answering what was asked. If you wish to offer alternatives you should do it as a comment, and not as an answer, because it isn't one. – vsync Aug 30 '16 at 08:45
  • @vsync I only mentioned the time in response to this part: "more than 450 open issues and basically abandoned". – Vicky Chijwani Aug 30 '16 at 09:54
30

try this:

$('.chzn-choices input').autocomplete({
  source: function( request, response ) {
    $.ajax({
      url: "/change/name/autocomplete/"+request.term+"/",
      dataType: "json",
      beforeSend: function(){$('ul.chzn-results').empty();},
      success: function( data ) {
        response( $.map( data, function( item ) {
          $('ul.chzn-results').append('<li class="active-result">' + item.name + '</li>');
        }));
      }
    });
  }
});
crizCraig
  • 8,487
  • 6
  • 54
  • 53
Ashirvad
  • 2,367
  • 1
  • 16
  • 20
  • Yes that solved my issue #2 in a clean way. Thanks! Now, do you have any thoughts on what's happening for #3? The javascript error inside the plugin itself ? – Lelly Aug 20 '12 at 20:52
  • hey the problem is not with plugin i think.. item is undefined here. can you tell when are you using autocomplete and when are you using chosen – Ashirvad Aug 20 '12 at 20:59
  • what do you mean? I'M using chosen on autocomplete $('.chzn-choices input').autocomplete({ and im implementing chosen on doc ready $(function() { $(".chzn-select").chosen(); I posted all my code above. – Lelly Aug 20 '12 at 21:03
  • I don't understand why this is the accepted answer. Does this actually work for you? The chosen library is throwing the following exception "Cannot call method 'lastIndexOf' of undefined". – senfo Jun 14 '13 at 12:40
  • 6
    The new version has a 'chosen-results' class instead of chzn-results – Ben Taliadoros Nov 08 '13 at 13:11
  • This gives me "Uncaught TypeError: Cannot read property 'substr' of undefined" when selecting an option – Emsal Feb 16 '21 at 14:00
23

Ashirvad's answer no longer works. Note the class name changes and using the option element instead of the li element. I've updated my answer to not use the deprecated "success" event, instead opting for .done():

$('.chosen-search input').autocomplete({
    minLength: 3,
    source: function( request, response ) {
        $.ajax({
            url: "/some/autocomplete/url/"+request.term,
            dataType: "json",
            beforeSend: function(){ $('ul.chosen-results').empty(); $("#CHOSEN_INPUT_FIELDID").empty(); }
        }).done(function( data ) {
                response( $.map( data, function( item ) {
                    $('#CHOSEN_INPUT_FIELDID').append('<option value="blah">' + item.name + '</option>');
                }));

               $("#CHOSEN_INPUT_FIELDID").trigger("chosen:updated");
        });
    }
});
  • `$("#CHOSEN_INPUT_FIELDID")` - what field is this? I have no input field with an ID – SHT Oct 03 '14 at 09:35
  • It's the id of the `select` element you've used with chosen. E.g., `$("#CHOSEN_INPUT_FIELDID").chosen(...)`. At least that's what it looks like from this code. – tjb1982 Mar 04 '15 at 20:42
13

This might be helpful. You have to just trigger an event.

$("#DropDownID").trigger("liszt:updated");

Where "DropDownID" is ID of <select>.

More info here: http://harvesthq.github.com/chosen/

Ivica Pesovski
  • 827
  • 7
  • 29
10

The Chosen plugin does not automatically update its list of options when the OPTION elements in the DOM change. You have to send it an event to trigger the update:

Pre Chosen 1.0: $('.chzn-select').trigger("liszt:updated");

Chosen 1.0 $('.chosen-select').trigger("chosen:updated");

If you are dynamically managing the OPTION elements, then you'll have to do this whenever the OPTIONs change. The way you do this will vary - in AngularJS, try something like this:

$scope.$watch(
  function() {
    return element.find('option').map(function() { return this.value }).get().join();
  }, 
  function() {
    element.trigger('liszt:updated');
  }
 }
Luke Stone
  • 129
  • 1
  • 3
  • Hi, I am currently having this issue and am using angular. I tried your solution but cant get it to work. PS: you forgot the closing tag for the watch – bohem.be May 08 '15 at 15:34
7

The chosen answer is outdated, same goes to meltingice /ajax-chosen plugin.

With Select2 plugin got many bugs which is i can't resolve it.

Here my answer for this question.

I integrated my solution with function trigger after user type. Thanks to this answer : https://stackoverflow.com/a/5926782/4319179.

//setup before functions
  var typingTimer;                //timer identifier
  var doneTypingInterval = 2000;  //time in ms (2 seconds)
  var selectID = 'YourSelectId';    //Hold select id
  var selectData = [];           // data for unique id array

  //on keyup, start the countdown
  $('#' + selectID + '_chosen .chosen-choices input').keyup(function(){

      // Change No Result Match text to Searching.
      $('#' + selectID + '_chosen .no-results').html('Searching = "'+ $('#' + selectID + '_chosen .chosen-choices input').val() + '"');


      clearTimeout(typingTimer);  //Refresh Timer on keyup 
      if ($('#' + selectID + '_chosen .chosen-choices input').val()) {

           typingTimer = setTimeout(doneTyping, doneTypingInterval);  //Set timer back if got value on input

      }

  });

  //user is "finished typing," do something
  function doneTyping () {

      var inputData = $('#' + selectID + '_chosen .chosen-choices input').val();  //get input data

      $.ajax({
        url: "YourUrl",
         data: {data: inputData},
        type:'POST',
        dataType: "json",
        beforeSend: function(){
          // Change No Result Match to Getting Data beforesend
          $('#' + selectID + '_chosen .no-results').html('Getting Data = "'+$('#' + selectID + '_chosen .chosen-choices input').val()+'"');
    },
        success: function( data ) { 

          // iterate data before append
          $.map( data, function( item ) {

            // matching data eg: by id or something unique; if data match: <option> not append - else: append <option>
            // This will prevent from select the same thing twice.
            if($.inArray(item.attr_hash,selectData) == -1){

              // if not match then append in select
              $('#' + selectID ).append('<option id="'+item.id+'" data-id = "'+item.id+'">' + item.data + '</option>');

            }            

          });

          // Update chosen again after append <option>
          $('#' + selectID ).trigger("chosen:updated");

        }
      });

  }

  // Chosen event listen on input change eg: after select data / deselect this function will be trigger
  $('#' + selectID ).on('change', function() {

    // get select jquery object
    var domArray = $('#' + selectID ).find('option:selected');

    // empty array data
    selectData = [];

    for (var i = 0, length = domArray.length; i < length; i++ ){

      // Push unique data to array (for matching purpose)
      selectData.push( $(domArray[i]).data('id') );

    }

    // Replace select <option> to only selected option
    $('#' + selectID ).html(domArray);

    // Update chosen again after replace selected <option>
    $('#' + selectID ).trigger("chosen:updated");

  });
Community
  • 1
  • 1
Afiq Abdullah
  • 106
  • 1
  • 7
2

Like Vicky suggested, Select2 comes with the AJAX features inbuilt and looks like a great plugin.

If you dont want to switch from Chosen, try AJAX-Chosen https://github.com/meltingice/ajax-chosen

Deepak Thomas
  • 3,355
  • 4
  • 37
  • 39
1

Chosen API has changed a lot.

If non of the solution given works for you, you can try this one: https://github.com/goFrendiAsgard/gofrendi.chosen.ajaxify

Here is the function:

// USAGE:
// $('#some_input_id').chosen();
// chosen_ajaxify('some_input_id', 'http://some_url.com/contain/');

// REQUEST WILL BE SENT TO THIS URL: http://some_url.com/contain/some_term

// AND THE EXPECTED RESULT (WHICH IS GOING TO BE POPULATED IN CHOSEN) IS IN JSON FORMAT
// CONTAINING AN ARRAY WHICH EACH ELEMENT HAS "value" AND "caption" KEY. EX:
// [{"value":"1", "caption":"Go Frendi Gunawan"}, {"value":"2", "caption":"Kira Yamato"}]

function chosen_ajaxify(id, ajax_url){
    console.log($('.chosen-search input').autocomplete);
    $('div#' + id + '_chosen .chosen-search input').keyup(function(){
        var keyword = $(this).val();
        var keyword_pattern = new RegExp(keyword, 'gi');
        $('div#' + id + '_chosen ul.chosen-results').empty();
        $("#"+id).empty();
        $.ajax({
            url: ajax_url + keyword,
            dataType: "json",
            success: function(response){
                // map, just as in functional programming :). Other way to say "foreach"
                $.map(response, function(item){
                    $('#'+id).append('<option value="' + item.value + '">' + item.caption + '</option>');
                });
                $("#"+id).trigger("chosen:updated");
                $('div#' + id + '_chosen').removeClass('chosen-container-single-nosearch');
                $('div#' + id + '_chosen .chosen-search input').val(keyword);
                $('div#' + id + '_chosen .chosen-search input').removeAttr('readonly');
                $('div#' + id + '_chosen .chosen-search input').focus();
                // put that underscores
                $('div#' + id + '_chosen .active-result').each(function(){
                    var html = $(this).html();
                    $(this).html(html.replace(keyword_pattern, function(matched){
                        return '<em>' + matched + '</em>';
                    }));
                });
            }
        });
    });
}

Here is your HTML:

<select id="ajax_select"></select>

And here is your javasscript:

// This is also how you usually use chosen
$('#ajax_select').chosen({allow_single_deselect:true, width:"200px", search_contains: true});
// And this one is how you add AJAX capability
chosen_ajaxify('ajax_select', 'server.php?keyword=');

For more information, please refer to https://github.com/goFrendiAsgard/gofrendi.chosen.ajaxify#how-to-use

goFrendiAsgard
  • 4,016
  • 8
  • 38
  • 64
1

The solution of @Ashivad mostly fixed my problem, but I needed to make this one-line addition to prevent the input to be erased after the results dropdown is displayed:

in success callback of the autocomplete added this line after triggering chosen:updated:

$combosearchChosen.find('input').val(request.term);

Complete listing:

var $combosearch = $('[data-combosearch]');
if (!$combosearch.length) {
  return;
}

var options = $combosearch.data('options');
console.log('combosearch', $combosearch, options);

$combosearch.chosen({
  no_results_text: "Oops, nothing found!",
  width: "60%"
});

// actual chosen container
var $combosearchChosen = $combosearch.next();

$combosearchChosen.find('input').autocomplete({
  source: function( request, response ) {
    $.ajax({
      url: options.remote_source + "&query=" + request.term,
      dataType: "json",
      beforeSend: function(){
        $('ul.chosen-results').empty();
      },
      success: function( data ) {
        response( $.map( data, function( item, index ) {
          $combosearch.append('<option value="' + item.id + '">' + item.label + '</option>');
        }));
        $combosearch.trigger('chosen:updated');
        $combosearchChosen.find('input').val(request.term);
      }
    });
  }
});
Alex Buznik
  • 686
  • 1
  • 6
  • 26
0

If you have two or more selects and use Steve McLenithan's answer, try to replace the first line with:

$('#CHOSENINPUTFIELDID_chosen > div > div input').autocomplete({

not remove suffix: _chosen

simeg
  • 1,889
  • 2
  • 26
  • 34
0
var object = $("#lstValue_chosen").find('.chosen-choices').find('input[type="text"]')[0];
var _KeyCode = event.which || event.keyCode;
if (_KeyCode != 37 && _KeyCode != 38 && _KeyCode != 39 && _KeyCode != 40) { 

    if (object.value != "") {
        var SelectedObjvalue = object.value;
        if (SelectedObjvalue.length > 0) {
            var obj = { value: SelectedObjvalue };
            var SelectedListValue = $('#lstValue').val();
            var Uniqueid = $('#uniqueid').val();

            $.ajax({
                url: '/Admin/GetUserListBox?SelectedValue=' + SelectedListValue + '&Uniqueid=' + Uniqueid,
                data: { value: SelectedObjvalue },
                type: 'GET',
                async: false,
                success: function (response) {
                    if (response.length > 0) {
                        $('#lstValue').html('');
                        var options = '';                            
                        $.each(response, function (i, obj) {
                            options += '<option value="' + obj.Value + '">' + obj.Text + '</option>';
                        });
                        $('#lstValue').append(options);
                        $('#lstValue').val(SelectedListValue);
                        $('#lstValue').trigger("chosen:updated");
                        object.value = SelectedObjvalue;
                    }
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    //jAlert("Error. Please, check the data.", " Deactivate User");
                    alert(error.StatusText);
                }
            });
        }
    }
}
0

If you are generating select tag from ajax, add this inside success function:

$('.chosen').chosen();

Or, If you are generating select tag on clicking add more button then add:

$('.chosen').chosen();

inside the function.

Pingolin
  • 3,161
  • 6
  • 25
  • 40
VikashSDNT
  • 41
  • 1
  • 6