Select2 dropdown but allow new values by user?
Asked Answered
G

9

137

I want to have a dropdown with a set of values but also allow the user to "select" a new value not listed there.

I see that select2 supports this if you are using it in tags mode, but is there a way to do it without using tags?

Guiltless answered 29/1, 2013 at 6:35 Comment(1)
Select2 never worked for me , at least createSearchChoice never worked for me in 4.0.3, and I didnt want my users to wait ajax to complete in order to return the same keyword , so I had to roll out my own library, I am only sharing it because I think it might help others who are still confused as I was , please dont down vote if you disagree with my answer: github.com/razzbee/tagcompleteCow
P
106

For version 4+ check this answer below by Kevin Brown

In Select2 3.5.2 and below, you can use something like:

$(selector).select2({
  minimumInputLength:1,
  "ajax": {
    data:function (term, page) {
      return { term:term, page:page };
    },
    dataType:"json",
    quietMillis:100,
    results: function (data, page) {
      return {results: data.results};
    },
    "url": url
  },
  id: function(object) {
    return object.text;
  },
  //Allow manually entered text in drop down.
  createSearchChoice:function(term, data) {
    if ( $(data).filter( function() {
      return this.text.localeCompare(term)===0;
    }).length===0) {
      return {id:term, text:term};
    }
  },
});

(taken from an answer on the select2 mailing list, but cannot find the link now)

Palestrina answered 18/2, 2013 at 20:57 Comment(9)
+1 awesome, just a comment, the new item is added to list only if I click enter?! Is it possible that the new value accepted when the select box lost focus (just a normal input box ! )Laddie
Adding the selectOnBlur: true will do the job please see: #25617020Laddie
Just a heads up for future readers, you probably want to be using tags: [] along with createSearchChoice.Princely
Trying something similar with multiple: true. Only works for the first new entry. If there is already one it will only let you choose from the list.Auscultation
Isn't data already filtered? Why not just if (data.length === 0)?Earsplitting
@Palestrina Why prefer jQuery's filter() method above native Array.prototype.filter()?Postpaid
@Postpaid I don't remember, if I had to guess, because I'm used to using jQueryPalestrina
How to display new value if not in select list? If the form is prepopulated with a the new value, the label is blank. I still want to display the value even if it is not in the list.Naiad
url is not defined.Situated
P
213

The excellent answer provided by @fmpwizard works for Select2 3.5.2 and below, but it will not work in 4.0.0.

Since very early on (but perhaps not as early as this question), Select2 has supported "tagging": where users can add in their own value if you allow them to. This can be enabled through the tags option, and you can play around with an example in the documentation.

$("select").select2({
  tags: true
});

By default, this will create an option that has the same text as the search term that they have entered. You can modify the object that is used if you are looking to mark it in a special way, or create the object remotely once it is selected.

$("select").select2({
  tags: true,
  createTag: function (params) {
    return {
      id: params.term,
      text: params.term,
      newOption: true
    }
  }
});

In addition to serving as an easy to spot flag on the object passed in through the select2:select event, the extra property also allows you to render the option slightly differently in the result. So if you wanted to visually signal the fact that it is a new option by putting "(new)" next to it, you could do something like this.

$("select").select2({
  tags: true,
  createTag: function (params) {
    return {
      id: params.term,
      text: params.term,
      newOption: true
    }
  },
  templateResult: function (data) {
    var $result = $("<span></span>");

    $result.text(data.text);

    if (data.newOption) {
      $result.append(" <em>(new)</em>");
    }

    return $result;
  }
});
Princely answered 4/5, 2015 at 0:40 Comment(6)
That was very helpful @Markus1980WienBollworm
I think I have used this snippet multiple time.Wareing
If not working double check you had add this option on select2 not add in ajax options. for select2 ajaxGambell
Its workign in version select2 (4.0.6) by this way: $("select").select2({ tags: true, createTag: function (params) { return { id: params.term, text: params.term, newOption: true } }, templateResult: function (data) { var result = data.text; if (data.newOption) { result = result + ' (new)'; } return result; } }); thanks @Kevin BrownOutguess
This should be the top response. I've been searching for a while for this, and this option answers every question I've seen on the topic.Bireme
The template result seems to only impact drop down values, the values put into the text area do not show the new suffix.Clearwing
P
106

For version 4+ check this answer below by Kevin Brown

In Select2 3.5.2 and below, you can use something like:

$(selector).select2({
  minimumInputLength:1,
  "ajax": {
    data:function (term, page) {
      return { term:term, page:page };
    },
    dataType:"json",
    quietMillis:100,
    results: function (data, page) {
      return {results: data.results};
    },
    "url": url
  },
  id: function(object) {
    return object.text;
  },
  //Allow manually entered text in drop down.
  createSearchChoice:function(term, data) {
    if ( $(data).filter( function() {
      return this.text.localeCompare(term)===0;
    }).length===0) {
      return {id:term, text:term};
    }
  },
});

(taken from an answer on the select2 mailing list, but cannot find the link now)

Palestrina answered 18/2, 2013 at 20:57 Comment(9)
+1 awesome, just a comment, the new item is added to list only if I click enter?! Is it possible that the new value accepted when the select box lost focus (just a normal input box ! )Laddie
Adding the selectOnBlur: true will do the job please see: #25617020Laddie
Just a heads up for future readers, you probably want to be using tags: [] along with createSearchChoice.Princely
Trying something similar with multiple: true. Only works for the first new entry. If there is already one it will only let you choose from the list.Auscultation
Isn't data already filtered? Why not just if (data.length === 0)?Earsplitting
@Palestrina Why prefer jQuery's filter() method above native Array.prototype.filter()?Postpaid
@Postpaid I don't remember, if I had to guess, because I'm used to using jQueryPalestrina
How to display new value if not in select list? If the form is prepopulated with a the new value, the label is blank. I still want to display the value even if it is not in the list.Naiad
url is not defined.Situated
O
14

Just for the sake of keep the code alive, I'm posting @rrauenza Fiddle's code from his comment.

HTML

<input type='hidden' id='tags' style='width:300px'/>

jQuery

$("#tags").select2({
    createSearchChoice:function(term, data) { 
        if ($(data).filter(function() { 
            return this.text.localeCompare(term)===0; 
        }).length===0) 
        {return {id:term, text:term};} 
    },
    multiple: false,
    data: [{id: 0, text: 'story'},{id: 1, text: 'bug'},{id: 2, text: 'task'}]
});
Origan answered 29/1, 2013 at 6:36 Comment(4)
I went to the fiddle, but it doesn't seem to be working for me in Chrome. Can you confirm?Triboluminescence
@Triboluminescence the code is working. the point in the fiddle is just to show how it should be done (the select is hidden in the fiddle)Origan
When I go to the fiddle I do not see a select2 dropdown anywhere. Wouldn't it be nice to have an example that actually... does something?Triboluminescence
How can i set the data from external source? I mean what if i want to load cities of a selected country and selected country itself is a dropodown?Lorolla
C
12

Since many of these answers don't work in 4.0+, if you are using ajax, you could have the server add the new value as an option. So it would work like this:

  1. User searches for value (which makes ajax request to server)
  2. If value found great, return the option. If not just have the server append that option like this: [{"text":" my NEW option)","id":"0"}]
  3. When the form is submitted just check to see if that option is in the db and if not create it before saving.
Cnut answered 27/7, 2015 at 5:50 Comment(0)
A
12

There is a better solution I think now

simply set tagging to true on the select options ?

tags: true

from https://select2.org/tagging

Aweather answered 10/5, 2019 at 9:38 Comment(0)
S
4

Improvent on @fmpwizard answer:

//Allow manually entered text in drop down.
createSearchChoice:function(term, data) {
  if ( $(data).filter( function() {
    return term.localeCompare(this.text)===0; //even if the this.text is undefined it works
  }).length===0) {
    return {id:term, text:term};
  }
},

//solution to this error: Uncaught TypeError: Cannot read property 'localeCompare' of undefined
Scandalmonger answered 10/1, 2015 at 12:36 Comment(1)
I used this with a slight modification I will put up my answer in a second but thanks.Mapes
M
3

Thanks for the help guys, I used the code below within Codeigniter I I am using version: 3.5.2 of select2.

var results = [];
var location_url = <?php echo json_encode(site_url('job/location')); ?>;
$('.location_select').select2({
    ajax: {
        url: location_url,
        dataType: 'json',
        quietMillis: 100,
        data: function (term) {
            return {
                term: term
            };
        },
        results: function (data) {
            results = [];
            $.each(data, function(index, item){
                results.push({
                    id: item.location_id,
                    text: item.location_name
                });
            });
            return {
                results: results
            };
        }
    },
    //Allow manually entered text in drop down.
    createSearchChoice:function(term, results) {
        if ($(results).filter( function() {
            return term.localeCompare(this.text)===0; 
        }).length===0) {
            return {id:term, text:term + ' [New]'};
        }
    },
});
Mapes answered 26/4, 2015 at 8:29 Comment(5)
I really really dont want to use ajax, this will only cause problems and cause me to do more work by adding a delete option. WHY IS THIS SO FREAKIN DIFFICULT. All they have to do is just add a "DONT CLEAR THE TEXTFIELD IF NO VALUE IS FOUND"Tendency
What sorts of problems? Been using Ajax for over 10 years now, never seemed to cause me the same level of alarm or concern it is causing yourself perhaps you can elaborate?Mapes
Yeah sure, the problems i will be facing is the fact, lets say for instance the user enters the wrong value, then that value has already been sent to the server to be added to the database. now you need to waste a restfull call and add an extra 15 min of work just to account for this (by sending another restfull call to the server telling it to delete this value). where as when you keep everything client side until the user posts the form nothing like this would happen. I ended up just using tags, which i did not want to do because its not 100% accurate with the designs i gave to the client.Tendency
add an extra 15 min of work - wow you must have some cheap ass client or projects that your unable to justify 15 minutes of extra work.Mapes
No that's not the point. Of course i will add that 15 min if necessary, i mean i had to now. The other problem now is that the designs are compromized... Other than that, I mean it could be completely avoided if a option like that was just there, but it seems like they went out of their way not to add it. That's why i find it frustrating...Tendency
M
3

I just stumbled upon this from Kevin Brown. https://mcmap.net/q/168399/-select-2-version-4-0-allow-user-to-enter-free-text

All you have to do for v4.0.6 is use tags: true parameter.

Machute answered 20/3, 2019 at 17:1 Comment(2)
What if you want to keep the format of the text box?Tendency
Sorry, don't know the answer. Been far too long and I don't currently use it.Machute
D
1
var text = 'New York Mills';
var term = 'new york mills';
return text.localeCompare(term)===0;

In most cases we need to compare values with insensitive register. And this code will return false, which will lead to the creation of duplicate records in the database. Moreover String.prototype.localeCompare () is not supported by browser Safary and this code will not work in this browser;

return this.text.localeCompare(term)===0;

will better replace to

return this.text.toLowerCase() === term.toLowerCase();
Definitely answered 10/2, 2015 at 9:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.