Select2 default options with ajax
Asked Answered
T

2

9

I have next html structure

<select class="change_item_dropdown_ajax form-control" id="item_id" name="item_id" onchange="updateData(this, this.value, 16)" >
<optgroup label="System Data">
  <option value="17">Test</option>
  <option selected="selected" value="18">System</option>
</optgroup>
</select>

Javascript

 $(".change_item_dropdown_ajax").select2({
        ajax: {
            url: "get_user_items",
            dataType: 'json',
            delay: 250,
            theme: "classic",
            data: function (params) {
                return {
                    q: { name_contains: params.term } // search term
                };
            },
            processResults: function (data) {
                return {
                    results: data
                };
            },
            cache: true
        },
        allowClear: true,
        placeholder: '--- Please select item ---',
        minimumInputLength: 1
    });

I want to make possibility for customer to see some default system options with items <optgroup label="System Data">, but also add ability to make search ajax queries by his own items.

However after binding select2 it doesn't show <optgroup label="System Data">...</optgroup>,

select2 options are empty and it only displays hint "Please enter 1 or more characters".

It's not even clear if it is possible to do, thanks.

UPD Related to select2 removes default options when using ajax

Select-2 removes options when using ajax adapter.

UPD2 github issue https://github.com/select2/select2/issues/3828

Truant answered 12/10, 2015 at 12:7 Comment(2)
So, you basically want to have an optgroup which is always visible, regardless what the client searches for, and below that, in another optgroup, you want to see the search results?Leghorn
user should see default values in dropdown under search input only if there is no text in inputTruant
L
1

If you want to have an <optgroup> which is always visible, regardless what the client searches for, and below that, in another <optgroup>, you want to see the search results, try this out:

In your server side script, where you are processing the search query the client typed in, you should return a json_encoded array with the items which you want to be displayed in the select box. Instead of this, try doing something like this:

** MODIFIED CODE PER YOUR REQUEST IN THE QUESTION COMMENT**

$q=$_REQUEST['q']; //the query string passed, note, that if you have minimumInputLength set, until that inputLength is reached, this part of the code won't run, so if you require the minimumInputLength, you should play with the templateResult option of select2
if (empty($q)) {
   $results=array();
   $result[0]=array(); //an optgroup to display when no query was sent
   $result[0]['text']='System Data';
   $result[0]['children']=array();
   // populate your first optgroup here, using the format which you are sending to select2.
} else {    
   $result[0]=array(); //an optgroup for results for the users searches
   $result[0]['text']='User results';
   $result[0]['children']=array();
   //populate the children array with the search related results
}

// json_encode the $result array, and return it

I am using a simmilar code, which works flawlesly at my side, but it is possible that some other things has to be set up before it will work(I wrote the code a few months ago, and I remember that I had a rather hard time figuring this out), if this approach doesn't work, notify me, and I will look into my code, to see how I did it exactly.

Leghorn answered 17/10, 2015 at 2:16 Comment(8)
Adam, this is not server-side problem, user should see default data before searching.Truant
With this solution, user will INDEED see default data, I do not know of any other solution. If you would like to show the default data only when the search input is empty, just return that data server-side when the query is empty. Select2 rebuilds the <select> element's html every time a new search is made, so if you want to have something that is there all the time, you have to handle that server-side.Leghorn
Adam, query is nor performed to server side when user didn't input text, again it shoild be fixed using client-sideTruant
I have this problem because ajax adapter removes existed options from select on select2 initialization process, which shouldn't be happenTruant
@Fivell, query is not performed, because you set that at least 1 characters should be inputed before a query is made. If you take out the minimumInputLength parameter when setting up select, or set it as 0, this will work. You will make 1 additional query each time when you click on the select box this way, but the desired result will be achieved. If you NEED the minimumInputLength 1 to make less queries, the only way to achieve the desired result is by using a kind of "hack", and playing with the "templateResult" parameters function.Leghorn
Adam, you're right but implementing this in server side is much more kind of "hack". I think there is clean solution for this problem using custom data adapter.Truant
Adam, no worries if there will be no client-side solution in 2 days, I will accept your answerTruant
@Fivell, you don't need to, if you think that this is not a working solution, but thanks:) In the meantime, I am trying to figure out a client-side only solution to this problem (or at least a solution, which let's you keep the minimumInputLength parameter ) :)Leghorn
W
3

Possible (and, unfortunately, I think the only) solution in this case is to write custom adapter. Something like this:

$.fn.select2.amd.define('select2/data/extended-ajax',['./ajax','../utils','jquery'], function(AjaxAdapter, Utils, $){

  function ExtendedAjaxAdapter ($element,options) {
    //we need explicitly process minimumInputLength value 
    //to decide should we use AjaxAdapter or return defaultResults,
    //so it is impossible to use MinimumLength decorator here
    this.minimumInputLength = options.get('minimumInputLength');
    this.defaultResults     = options.get('defaultResults');

    ExtendedAjaxAdapter.__super__.constructor.call(this,$element,options);
  }

  Utils.Extend(ExtendedAjaxAdapter,AjaxAdapter);

  //override original query function to support default results
  var originQuery = AjaxAdapter.prototype.query;

  ExtendedAjaxAdapter.prototype.query = function (params, callback) {
    var defaultResults = (typeof this.defaultResults == 'function') ? this.defaultResults.call(this) : this.defaultResults;
    if (defaultResults && defaultResults.length && (!params.term || params.term.length < this.minimumInputLength)){
      var processedResults = this.processResults(defaultResults,params.term);
      callback(processedResults);
    }
    else {
      originQuery.call(this, params, callback);
    }
  };

  return ExtendedAjaxAdapter;
});

https://gist.github.com/govorov/3ee75f54170735153349b0a430581195

This adapter first analyzes query term. AjaxAdapter will be kicked in if term is longer than minimumInputValue. Otherwise, defaultResults will be passed to the callback.

To use this adapter, just pass this to select2 options:

dataAdapter: $.fn.select2.amd.require('select2/data/extended-ajax')

I'm not using requireJS or AMD, if you do, use it to require adapter.

Whole answered 19/5, 2016 at 21:20 Comment(0)
L
1

If you want to have an <optgroup> which is always visible, regardless what the client searches for, and below that, in another <optgroup>, you want to see the search results, try this out:

In your server side script, where you are processing the search query the client typed in, you should return a json_encoded array with the items which you want to be displayed in the select box. Instead of this, try doing something like this:

** MODIFIED CODE PER YOUR REQUEST IN THE QUESTION COMMENT**

$q=$_REQUEST['q']; //the query string passed, note, that if you have minimumInputLength set, until that inputLength is reached, this part of the code won't run, so if you require the minimumInputLength, you should play with the templateResult option of select2
if (empty($q)) {
   $results=array();
   $result[0]=array(); //an optgroup to display when no query was sent
   $result[0]['text']='System Data';
   $result[0]['children']=array();
   // populate your first optgroup here, using the format which you are sending to select2.
} else {    
   $result[0]=array(); //an optgroup for results for the users searches
   $result[0]['text']='User results';
   $result[0]['children']=array();
   //populate the children array with the search related results
}

// json_encode the $result array, and return it

I am using a simmilar code, which works flawlesly at my side, but it is possible that some other things has to be set up before it will work(I wrote the code a few months ago, and I remember that I had a rather hard time figuring this out), if this approach doesn't work, notify me, and I will look into my code, to see how I did it exactly.

Leghorn answered 17/10, 2015 at 2:16 Comment(8)
Adam, this is not server-side problem, user should see default data before searching.Truant
With this solution, user will INDEED see default data, I do not know of any other solution. If you would like to show the default data only when the search input is empty, just return that data server-side when the query is empty. Select2 rebuilds the <select> element's html every time a new search is made, so if you want to have something that is there all the time, you have to handle that server-side.Leghorn
Adam, query is nor performed to server side when user didn't input text, again it shoild be fixed using client-sideTruant
I have this problem because ajax adapter removes existed options from select on select2 initialization process, which shouldn't be happenTruant
@Fivell, query is not performed, because you set that at least 1 characters should be inputed before a query is made. If you take out the minimumInputLength parameter when setting up select, or set it as 0, this will work. You will make 1 additional query each time when you click on the select box this way, but the desired result will be achieved. If you NEED the minimumInputLength 1 to make less queries, the only way to achieve the desired result is by using a kind of "hack", and playing with the "templateResult" parameters function.Leghorn
Adam, you're right but implementing this in server side is much more kind of "hack". I think there is clean solution for this problem using custom data adapter.Truant
Adam, no worries if there will be no client-side solution in 2 days, I will accept your answerTruant
@Fivell, you don't need to, if you think that this is not a working solution, but thanks:) In the meantime, I am trying to figure out a client-side only solution to this problem (or at least a solution, which let's you keep the minimumInputLength parameter ) :)Leghorn

© 2022 - 2024 — McMap. All rights reserved.