Issue
I just started using Select2 (http://ivaynberg.github.io/select2/) and I am trying to do a basic task.
I have a select box that has, for example, 3 items in it. I want to be able to have the user either select 1 of the 3 results or type in their own result and then eventually, on submit, it will submit whatever value is in the box.
What I've Tried
<input style="width: 200px;" type="hidden" id="foo" />
<script type="text/javascript">
$(document).ready(function () {
$("#foo").select2({
query: function (query) {
var data = { results: [{ text: 'math' }, { text: 'science' }, { text: 'english' }] };
data.results.push({ text: query.term });
query.callback(data);
}
});
});
</script>
The code above allows me to see the 3 results and type in a result myself. But I am unable to get the typed in result to "stick" when I click away, hit enter, or select the result I just typed in. Same goes for the select options, but I am most concerned with the user inputted text.
Here's what it looks like:
The parameter createSearchChoice allows you to do just what you want. Here is an example:
<script type="text/javascript">
$("#foo").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'}]
});
</script>
Taken from a closed issue at: https://github.com/ivaynberg/select2/issues/201
Fiddle: http://jsfiddle.net/pHSdP/2/
Also, make sure you add a name to the input, otherwise you won't see the value at server side
<input style="width: 200px;" type="hidden" id="foo" name="foo" />
Just a quick note for anyone else who's having a different data input. In case the console says " this.text is undefined ", make sure you check your text tag, like this:
<script type="text/javascript">
// Data input taken from "label", not "text" like usual
var lstData = [{id: 0, 'label': 'story'},{id: 1, 'label': 'bug'},{id: 2, 'label': 'task'}];
$("#foo").select2({
createSearchChoice:function(term, data) {
if ($(data).filter(function() {
return this.label.localeCompare(term)===0; }).length===0) {
return {id:term, 'label':term};
}
},
data: { results: lstData, text: 'label' }
});
</script>
Library you are using is used to filter options in a select box. It doesn't take new input, as per their own documentation:
Select2 is a jQuery based replacement for select boxes.It supports searching, remote data sets, and infinite scrolling of results.
I would suggest you to use jQueryUI Autocomplete or TypeAhead
Related
I have a slickgrid cell with an autocompletion and a custom formatter. The cell value is a key field of dynamically loading autocompletion list. The list is shown as labels (e.g. Contract: ABCDEF-123, thick:10, width:210, City: Chicago) and when I select one it appears in the according input field. The point is that the formatter does not know that label, it only knows the key (order_id).
function contractFormatter(row, cell, value, columnDef, dataContext) {
var res = '';
var val = get_contract_list()[value] ? get_contract_list()[value]['label'] : '';
res = '<span class="' + getSpanClass() + '" style="float: left;"></span>\n\
'+ (val =='' ? '<span style="color:slategrey;"> Enter 3 symbols </span>' + val : val) + '';
return res;
}
The function get_contract_list returns the whole list of contracts and it is very big, so it was decided to make that list dynamic. So the function is empty now and it would be nice just to take the selected label into val.
Is there any way to achieve it?
You have to remember that Formatters are synchronous and it must return right away in a string format, if it requires a lot of processing power while staying synchronous then you'll end up slowing down your grid. You should probably cache your list once in a separate variable and use it afterward instead of reloading the list every time. If you load something that takes time and is asynchronous (a delayed output) then you'll want to look up the asyncPostRenderer (you can see this Example)
So going back to displaying the associated key to a label, I've done something similar in my lib in this Example and a live demo here, in my use case the value is a key index and I use the complexityLevelList to find its associated object which I can then read its label to display in the formatter.
export class MyExample {
complexityLevelList = [
{ value: 0, label: 'Very Simple' },
{ value: 1, label: 'Simple' },
{ value: 2, label: 'Straightforward' },
{ value: 3, label: 'Complex' },
{ value: 4, label: 'Very Complex' },
];
prepareGrid() {
this.columnDefinitions = [
{
id: 'complexity', name: 'Complexity', field: 'complexity', minWidth: 100,
formatter: (_row, _cell, value) => this.complexityLevelList[value].label,
filter: {
model: Filters.multipleSelect,
collection: this.complexityLevelList
},
editor: {
model: Editors.singleSelect,
collection: this.complexityLevelList,
massUpdate: true
},
},
];
}
}
Note that the Filters & Editors are specific to my lib Slickgrid-Universal, but you should get the idea on how to refactor your code to make it work.
I don't want select2 multi select drop down loose data in page refresh. select2 drop down should maintain state
below is my code. its working fine, I have only problem that select2 is losing selection/data if we refresh the page.My requirement is even after page refresh it should not remove there values/or empty.
$processOrderNbr.select2({
ajax: getProcessOrderNbr(),
minimumInputLength: 4,
placeholder: " ",
allowClear: false,
multiple: true
});
function getProcessOrderNbr() {
return {
url: 'GetProcessOrder',
dataType: 'json',
delay: 250,
data: function (params) {
return {
searchKeyword: params
};
},
results: function (data) {
return {
results: $.map(data, function (item) {
return {
text: item,
id: item
}
})
};
}
};
}
You need to put them again into the select2 boxes.
I had a similar issue, where I work with window.sessionStorage to store the selected values. After reloading the page, I set the values stored in the session storage into the select2 boxes with this code:
var selectionList = [{ id: window.sessionStorage.getItem("searchValue"), text: window.sessionStorage.getItem("searchValue")}];
$("#searchValue").select2({
data: selectionList
});
$('#searchValue').val(window.sessionStorage.getItem("searchValue")).trigger("change");
To store the values in the window.sessionStorage you can use this code:
$('#searchValue').select2({
...
.on("change", function (e) {
window.sessionStorage.setItem("searchValue", $(this).val());
}
...
Maybe this solution helps you in your case, but someone knows a better solution?
If you need to show the previous wrote value after reloading the page without losing the data that is the solution.
var dataCategory = JSON.parse(sessionStorage.getItem('category'));
var select = $("#category");
select.select2({
tags: true,
allowClear: true,
maximumInputLength: 30,
maximumSelectionLength: 20,
tokenSeparators: [',', ' '],
data: dataCategory
}).on("change", function (e) {
var selected = []; // create an array to hold all currently wrote by user
// loop through each option
$('#category option').each(function() {
// if it's selected, add it to the array above
if (this.selected) {
selected.push(this.value);
}
});
// store the array of selected options
sessionStorage.setItem('category', JSON.stringify(selected));
});
// set and show the previous wrote keywords
var dataCategoryLength = dataCategory.length;
for (var i =0; i < dataCategoryLength; i++) {
$('#category').val(dataCategory).trigger('change');
}
I've read almost every article i could find on how to accomplish this, but i'm still failing miserably. mostly because i'm an amateur at jQuery/Javascript.
I have a website that contains one input element. I've managed to get jQuery Autocomplete working nicely on this. The problem is that when i dynamically add additional elements using the .append method, these new elements do not work with autocomplete.
See jsfiddle: http://jsfiddle.net/aktive/r08m8vvy/
see jsfiddle for full code sample
Thankyou in advance for your help!! :)
-Dean
You must bind autocomplete after adding new elements
$(wrapper).find('input[type=text]:last').autocomplete({
source: availableAttributes
});
See example: http://jsfiddle.net/r08m8vvy/4/
I actually found that a more reliable way was to bind using 'on' with the 'focus' action to init the auto complete based on the field class and destory it on exit. This way it cleans up the garbage and only inits it once you need to.
I was using it with cloning rows though and even doing deep cloning, which would clone the plus and minus buttons for the rows, it wasn't cloning the autocomplete.
var auto_opts = {...opts...}
$('body').on('focus', '.search_field', function(){
$(this).autocomplete(auto_opts).on('blur', function(){$(this).autocomplete('destroy')});
});
It also means that you aren't forced into using the last row because it works on the field as you focus on it
See http://jsfiddle.net/r08m8vvy/2/
Give the new input an ID and call autocomplete on it. The initial autocompate call you make won't include the dynamically added inputs.
$(wrapper).append('<div><input id="' + x + '" type="text" name="mytext"/>Remove</div>'); //add input box
$( "input[id="+ x +"]" ).autocomplete({
source: availableAttributes
});
I Updated the fiddle http://jsfiddle.net/r08m8vvy/5/
You have to bind the autocomplete for new element
$(wrapper).append($('<div><input type="text" name="mytext[]"/>Remove</div>').find(":text").autocomplete({
source: availableAttributes
}));
Here is the simpler way to use autocomplete for dynamically created input fields.
$('body').on('focus', '.dynamic-input-class', function (e) {
$(this).autocomplete({
minLength: 2,
delay: 500,
source: function (request, response) {
$.ajax( {
url: "server side path that returns json data",
data: { searchText: request.term},
type: "POST",
dataType: "json",
success: function( data ) {
$("#data-success").html(data.returnedData); //returnedData is json data return from server side response
}
});
}
});
});
<script>
$(document).ready(function() {
var nhlTeams = ['Atlanta', 'Boston', 'Buffalo', 'Calgary', 'Carolina', 'Chicago', 'Colorado', 'Columbus', 'Dallas', 'Detroit', 'Edmonton', 'Florida', 'Los Angeles', 'Minnesota', 'Montreal', 'Nashville', ];
var nbaTeams = ['New Jersey', 'New Rork', 'New York', 'Ottawa', 'Philadelphia', 'Phoenix', 'Pittsburgh', 'Saint Louis', 'San Jose', 'Tampa Bay', 'Toronto Maple', 'Vancouver', 'Washington'];
var nhl = $.map(nhlTeams, function (team) { return { value: team, data: { category: 'Section A' }}; });
var nba = $.map(nbaTeams, function (team) { return { value: team, data: { category: 'Section B' } }; });
var teams = nhl.concat(nba);
// Initialize autocomplete with local lookup:
$('body').on('focus', '.db_items', function(){
$(this).autocomplete({
lookup: teams,
minChars: 1,
onSelect: function (suggestion) {
$('#selection').html('You selected: ' + suggestion.value);
},
showNoSuggestionNotice: true,
noSuggestionNotice: 'Sorry, no matching results',
groupBy: 'category'
});
});
});
</script>
I am using Select2 with Jquery-editable and encountering an abnormal behavior of Select2, what I am doing is displaying editable table of information using ejs template, and as user clicks on CBA opens up a select2 box which have the originally selected result, and then user can add or delete options in it, options comes from Database source, and when user selects an options it adds an empty option in database with the selected option , the array looks like this
[ "ABCD", "ONAB", "" , "BCNU" ]
I read somewhere about allowClear: true and add a placeHolder but It doesn't helped me at all. As everything is done dynamically I can't find where that empty option is added.
Code is below:
Ejs/HTML code for Select 2
<tr>
<td width="40%">Select CBA(s)</td>
<td>
<a class="cbaSelectUnit" data-emptytext="Select CBA(s)" data-original-title="Select CBA(s)" data-type="select2"></a>
</td>
Javascript for Select 2
$("a[data-name='Cba']").editable({
showbuttons: 'false',
emptytext: 'None',
display: function(values) {
var html = [];
html.push(values);
$(this).html(html);
},
select2: {
multiple: true,
allowClear: true,
placeholder: "Select CBA(s)",
ajax: {
// url is copied from data-source via x-editable option-passing mechanism
dataType: 'json',
// pass the '?format=select2' parameter to API call for the select2-specific format
data: function(term, page) {
return {
deptId: departmentId,
format: 'select2'
};
},
// transform returned results into the format used by select2
results: function(data, page) {
return {
results: data
};
}
},
// what is shown in the list
formatResult: function(cba) {
return cba.text;
},
// what will appear in the selected tag box
formatSelection: function(cba) {
return cba.text;
},
// rendering id of the values to data.value requirement for Select 2
id: function(cba) {
return cba.value;
},
// what is shown in the selected-tags box
initSelection: function(element, callback) {
var id = $(element).val(),
result = id.replace(/^,\s*$/, ',').split(",").map(function(v) {
return {
id: v,
text: v
};
});
callback(result);
}
}
});
Format in which Code is returned from the database:-
Facility.findOne({ _id: department.Facility }, function(err, facility) {
if (err) {
res.send(500, err);
} else if (!facility) {
res.send(404, 'Facility not found');
} else if (req.query.format && req.query.format === 'select2') {
var result = facility.Cba.map(function(c) {
return { value: c, text: c };
});
res.json(result);
}
});
Image showing an empty box added by itself
How Array looks after I edit
So it was just a simple syntax error, I was doing found out by myself,
I was returning cba.value as id, but the initSelection was returning
{id: v, text: v}
it should be value & text instead of id & text.
// what is shown in the selected-tags box
initSelection: function(element, callback) {
var id = $(element).val(),
result = id.replace(/^,\s*$/, ',').split(",").map(function(v) {
return {
value: v,
text: v
};
});
callback(result);
}
I use jQuery select2 plugin in order to retrieve postcodes using the provided ajax callback function as follows:
$(document).ready(function() {
$("#postcodes").select2({
placeholder : "Search for a postcode",
multiple : true,
minimumInputLength : 3,
ajax : {
url : "/bignibou/utils/findGeolocationPostcodeByPostcodeStartingWith.json",
dataType : 'json',
data : function(term) {
return {
postcode : term
};
},
results : function(data) {
console.log(data);
return {
results : $.map(data, function(item) {
return {
id : item.id,
text : item.postcode
};
})
};
}
}
});
});
Once two postcodes are selected I get the resulting hidden input in the DOM:
<input type="hidden" class="bigdrop select2-offscreen" id="postcodes" style="width:600px" name="familyAdvertisement.postcodes" value="4797,4798" tabindex="-1">
The issue I have is that once the form is redisplayed (for instance in the event of some other controls being in error), the selections (i.e. the two postcodes and especially the text) don't show in the form although the hidden input does have the two values (i.e. 4797 and 4798, which are the ids for the postcode).
I am not sure if I have to do another ajax round trip when the form is redisplayed or if there is a better way to go.
Can anyone please advise?
The initSelection method has to pass the values which has to be present in the select2
Ex:
$("#postcodes").select2({
placeholder : "Search for a postcode",
multiple : true,
minimumInputLength : 1,
data:[],
initSelection : function (element, callback) {
var data = [{id:1,text:'bug'},{id:2,text:'duplicate'}];
callback(data);
}
}).select2('val', ['1', '2']);
Demo: Fiddle