I'm trying to use the newest version of Select2 to query my site's API and return a multiple-select. It's fetching the right data and it's even formatting the dropdown properly, combining two keys in the returned objects as "(A-123) John Johnson", but the dropdown's not responding to mouseover or clicks.
I'm using the select2.full.min.js and select2.min.css files. For the purposes of the project, I'm including them in the directory and accessing them through Bundles in cshtml, instead of accessing them via a CDN.
HTML:
<div>
<select class="user-list" multiple="multiple" style="width: 100%">
</select>
</div>
At the moment, it's looking like this, which is how I want it:
Not sure if this is relevant, but when I browse the generated source code while searching, the input looks fine, but the dropdown's code is greyed out as though it were hidden.
Per other suggestions I've found on here and elsewhere, I've tried a few different solutions. I eventually found out how templateResult and templateSelection are supposed to work (not particularly thanks to the documentation), and tried returning the ID as well, but I still can't seem to actually select anything, or even get a response when I hover over the options.
Here's what I wound up with, including some debugging to make sure the returned object is valid.
JS:
// Setup autocomplete/select for User filter
$(".user-list").select2({
ajax: {
url: "[api url]",
type: "GET",
dataType: "json",
data: function (params) {
return {
search: params.term, // search term
page: params.page
};
},
processResults: function (data) {
console.log(JSON.stringify(data));
return {
results: data
};
},
},
escapeMarkup: function (markup) { return markup; },
id: function (data) { return data.Id.toString(); },
minimumInputLength: 1,
templateResult: function (data, page) {
return "(" + data.User + ") " + data.Name;
},
templateSelection: function (data, page) {
return "(" + data.User + ") " + data.Name;
}
})
The ID is a GUID, and there are two other fields, which I'll call Name and User.
Data Sample:
[
{
"Id":"a1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1",
"Name":"John Johnson",
"User":"A-123"
},
{
"Id":"b2b2b2b2-b2b2-b2b2-b2b2-b2b2b2b2b2b2",
"Name":"Tom Thompson",
"User":"B-456"
}
]
I hate to add to the pile of questions that seem to be about this, but most results I've found have been for the older version with significantly different options, and they just haven't worked for me yet.
The way select2 operates is that it uses the "id" values of each data object and puts those into the original Select element as the selected option(s). This is case-sensitive.
By default, it displays the dropdown options and the selected element by whatever the "text" value is of the data object. This does not allow for custom formatting.
If (like me) you want to return different data options, you still need to return a field as "id", or re-map a field to "id" in an object returned in the processResults option under ajax. Then use the templateResult and templateSelection settings with your other returned data to display what you want.
If you return and parse everything correctly except for the id, you may wind up with an otherwise functional list, but be unable to select any options.
The requirements for the dropdown changed a bit with my project, but here's where I wound up. It works fine the multiple="multiple" attribute added to to make it a multi-select, too.
<select class="select2" style="width:100%; height: 100%;">
<option></option>
</select>
$(".select2").select2({
ajax: {
url: "[api url]",
method: "get",
data: function (params) {
return {
search: params.term
};
},
processResults: function (data) {
return {
results: data
};
},
cache: true
},
placeholder: "Enter a User ID or Name",
templateResult: function(data) {
return "(" + data.user + ") " + data.name;
},
templateSelection: function(data) {
return "(" + data.user + ") " + data.name;
}
}).ready(function () {
$(".select2-selection__placeholder").text("Enter a User ID or Name")
})
It's possible part of my initial issue was all the attempts at implementing fixes for older versions of Select2, which had a completely different set of options/settings available.
Additionally, a bit of a side-note, the "placeholder" attribute doesn't currently work with custom templating. It tries to force the placeholder text into the Result format, which shows "(undefined) undefined" with this code. To fix it requires an empty option and to replace the text on select2.ready.
I had the same problem. Solution:
added this part :
_.each(data.ResultObject, function (item) { item.id = item.K_CONTACT; });
(used underscore)
for my init
$(".js-data-example-ajax").select2({
ajax: {
url: "api/MyApi/MyApi",
method: "POST",
dataType: 'json',
delay: 250,
data: function (params) {
return {
SearchPart: params.term, // search term
page: params.page
};
},
processResults: function (data, params) {
params.page = params.page || 1;
_.each(data.ResultObject, function (item) { item.id = item.K_CONTACT; });
return {
results: data.ResultObject,
pagination: {
more: (params.page * 30) < data.total_count
}
};
},
cache: true
},
multiple: true,
escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
minimumInputLength: 3,
tags: true,
templateResult: self.Select2FormatData, // omitted for brevity, see the source of this page
templateSelection: self.Select2FormatDataSelection // omitted for brevity, see the source of this page
});
Related
I am working on jquery autocomplete in mvc platform. Now, my question is that in some textbox data autocomplete is working properly while in some textbox data autocomplete is not working properly.
For more clear, lets see the image of autocomplete task
now as per the image when I write the R word, I am getting the suggestion list of related R word.
now as per the second image I have written the whole word but still suggestion is not display.
Here is my code,
View
<input type="text" id="CustomerName" name="CustomerName" required data-provide="typeahead" class="typeahead search-query form-control autocomplete"
placeholder="Customer Name" />
<script>
$(document).ready(function () {
$.ajax({
url: "/ServiceJob/CustomerSerchAutoComplete",
method: "GET",
dataType: "json",
minLength: 2,
multiple: true,
success: function (data) {
/*initiate the autocomplete function on the "myInput" element, and pass along the countries array as possible autocomplete values:*/
//autocomplete(document.getElementById("CustomerName"), data.data);
$('#CustomerName').autocomplete({ source: data.data, minLength: 2, multiple: true });
}
});
});
</script>
Controller
[HttpGet]
public IActionResult CustomerSerchAutoComplete()
{
var customers = _Db.Ledger.Where(x => x.LedgerTypeId == (int)LedgerType.Customer).ToList();
var result = (from n in customers
select new
{
kk = n.Name.ToUpper()
}).ToList();
return Json(new { data = result });
}
As you are getting
Uncaught TypeError: Cannot read property 'substr' of undefined
For this error you should check that data is not null.
success: function (data) {
if(data != null)
{
autocomplete(document.getElementById("CustomerName"), data.data);
}
}
I want to test with Behat a select2 dropbox that makes an ajax call in order to get the results.
The problem is that immediately after I populate the search box of select2 the dropdown closes immediately so the search is not happening.
If the select is already populated (a normal dropdown with predefined values) everything is ok because all the data is there and it takes it right away.
I'm using Behat Page object for my project so here is my method:
select2FieldPopulate
public function select2FieldPopulate($field, $value)
{
$select2Field = $this->find('css', '.'.$field);
//check if select2Field exists
if (!$select2Field) {
throw new \Exception(sprintf("Field %s was not found", $field));
}
$select2Field->click();
$select2Input = $this->find('css', '.select2-drop.select2-drop-active .select2-search input.select2-input');
if (!$select2Input) {
throw new \Exception(sprintf("Field %s was not found", "select2-input"));
}
$select2Input->setValue($value);
}
js
function buildSelect2Element(selector, placeholder, url) {
var element = $(selector).select2({
placeholder: placeholder,
minimumInputLength: 3,
ajax: {
url: url,
dataType: 'json',
data: function (term) {
return {
q: term
}
},
results: function (data) {
//workarround to fix select2
var results = [];
$.each(data, function (index, item) {
results.push({
id: item.id,
text: item.name
});
});
return {
results
}
}
}
});
return element;
}
On $select2Input->setValue() the search box gets populated with the value but the search does not happen because the dropdown closes right away.
So the question is: Is there a way to force the box to stay open until the results are displayed (the ajax call is finished)?
I managed to make it work under select2 v4.x.
I added to the js the option to select on close like this:
js
function buildSelect2Element(selector, placeholder, url) {
var element = $(selector).select2({
theme: "classic",
placeholder: placeholder,
minimumInputLength: 3,
selectOnClose: true, //HERE
Then in my tests i used the evaluateScript method:
select2FieldPopulate method
$this->getDriver()->evaluateScript("$('#your_select2_element').select2('open')");
$this->getDriver()->evaluateScript("$('.select2-search__field').val('". $value ."').keyup();");
$this->getDriver()->evaluateScript("$('#your_select2_element').select2('close')");
I have a scenario as follows,
Need to put autocomplete functionality on dynamic textbox with onkeyup functionality
My code is as follows, Here i have invoked a function "GetName" on buttonclick where am loadin the dynamic textboxes
function GetName() {
var dataToSend = JSON.stringify({ prefixText: $('#search').val(), Id: $("#SearchType").val()
});
$.ajax({
type: "POST",
data: { jsonData: dataToSend },
url: "GetName",
datatype: "json",
success: function (result) {
$("#ResourceNames").empty();
$("#ResourceNames").append('<table>');
$.each(result, function (i, Name) {
$("#ResourceNames").append('<tr><td ><Label>' + Name.Value + '</label></td><td> <input type="text" id="Supervisor" class = "form-control", onkeyup="GetResource(\'' + Name.Text + '\');"/></td></tr>');
});
},
error: function (xhr, status) {
alert(status);
}
})
$("#ResourceNames").append('</table>');
}
Here onkeyup event of textbox supervisor am calling the below function getresource with an argument
function GetResource(i) {
debugger;
var dataToSend = JSON.stringify({ prefixText: $("#Supervisor").val(), designation: i });
$.ajax({
url: "GetSupervisor",
data: { jsonData: dataToSend },
dataType: 'json',
type: 'POST',
success: function (data) {
$("#Supervisor").autocomplete({source:data});
});
},
error: function (error) {
alert('error; ' + error.text);
}
});
}
am not able to bind autocomplete data to dynamic textbox, can anyone help me out on the same?
You have several issues in your code:
First of all jQuery doesn't concatenates strings into DOM, it creates a DOMElement. So,
$("#ResourceNames").append('<table>'); will append an entire <table> element. Anything you append to #ResourceNames will be added after the table, not inside it.
The HTML you're appending contains id. So there can be multiple elements with same id which is invalid, depending on the response. It's better to use a common classname for those elements instead.
You don't need to manually handle the keyup event. You can specify the url you want to hit as the value of the source option of autocomplete, provided it returns a response suitable for the autocomplete. see this example in docs.
Instead of passing the value via the inline handler, you can store the value as a data-* attribute and access it later.
So your code should be something along:
function GetName() {
var dataToSend = JSON.stringify({ prefixText: $('#search').val(), Id: $("#SearchType").val()});
$.ajax({
type: "POST",
data: { jsonData: dataToSend },
url: "GetName",
datatype: "json",
success: function (result) {
var htmlString ="<table>";
$.each(result, function (i, Name) {
htmlString +="<tr><td ><Label>" + Name.Value + "</label></td><td> <input type='text' class = 'form-control Supervisor' data-name='"+ Name.Text + "'/></td></tr>";
});
},
error: function (xhr, status) {
alert(status);
}
})
$("#ResourceNames").append(htmlString);
$(".Supervisor")..autocomplete({
source: "GetSupervisor" // where GetSupervisor is your data source
});
}
If you want to manually send requests along with data and pass the results into the autocomplete, you can specify an function as the value of source option. (See my another answer for more info). for example:
$(".Supervisor").autocomplete({
source: function(request,response){
/*send the request here.
request.term contains the current value entered in textfield
pass the results you want to display like response(data)*/
}
});
Read the API documentation, play with it for a while and you'll be able to get it working.
I'm integrating a Google Map API that uses Geonames and Select2 to allow the user to enter the cities that he/she has visited.
Currently, I am trying to find a way for the search area to show the selections the user made in a previous session upon reloading the page (e.g., if the user already entered Paris, France in a previous session, then Paris, France should be preloaded in the search area upon reloading). The selections are stored in a database, but at the moment I'm only able to put one of the previously selected cities in the search area by repassing it through Geonames (I need to pass through Geonames to pass the lat & long). I'd like to repass as many locations as the user entered in the previous session.
The code I am using for this is below - thanks for your help:
function locationFormatResult(location) {
return location.name + ", " + location.adminName1 + ", " + location.countryName;
}//results format
function locationFormatSelection(location) {
return location.name + ", " + location.adminName1 + ", " + location.countryName;
}//selection format
$(function () {
$('#citiestext').select2({
id: function(e) { return e.name + ',' + e.adminName1 + ',' + e.countryName + ',' + e.lat + ',' + e.lng},
placeholder: 'Location',
multiple: true,
allowClear: true,
width: '350px',
height: '50px',
overflow: 'auto',
minimumInputLength: 2,
ajax: { //this is the ajax call for when the user selects a city
url: 'http://ws.geonames.org/searchJSON',
dataType: 'jsonp',
data: function (term) {
return {
featureClass: "P",
style: "medium",
isNameRequired: "true",
q: term
};
},
results: function (data) {
return {
results: data.geonames
};
}
},
initSelection : function(element, callback){
for (var i=11;i<13;i++){
$.ajax("http://ws.geonames.org/searchJSON",{//ajax for preloading
dataType: 'jsonp',
data:{
maxRows:1,
q: i}//for this example, I'm using the numbers 11 & 12 as my Geonames queries just to test the preloading functionality (both numbers do have corresponding locations in Geonames if run through a query)
}).done(function(data){callback(data.geonames);}); //PROBLEM: currently is only returning the location for "12" - I need it to return locations for 11 and 12 in the select area
}},
formatResult: locationFormatResult,
formatSelection: locationFormatSelection,
dropdownCssClass: "bigdrop",
escapeMarkup: function (m) { return m; }
});
});
jsfiddle: http://jsfiddle.net/YDJee/ (trying to get more than one entry in the select box)
The point is that the callback needs to be called with the array of objects for multiple select2.
In this case, as you need to gather each json object from an ajax call, you need to use jQuery deferreds.
Something like that:
initSelection : function(element, callback){
var result = new Array();
var f = function(i) {
return $.ajax("http://ws.geonames.org/searchJSON",{
dataType: 'jsonp',
data:{
maxRows:1,
q: i
},
success: function(data) { result.push(data);}})
};
var def = [];
for (var i=11;i<13;i++){
def.push(f(i))
}};
$.when.apply($, def).done(function() {
callback(result);
});
}
For those also using an id callback, and who find their issue is not an async problem, take a look at this (which remains a necessary fix as of this posting).
I have a geo collection that contains items like:
[state name]
[city], [state]
[country]
A text box is available for a user to begin typing, and a jQuery autocomplete box fills displays possible options.
The URL structure of the post request will depend on which was selected from the collection above, ie
www.mysite.com/allstates/someterms (if a country is selected)
www.mysite.com/city-state/someterms (if a city, state is selected)
www.mysite.com/[state name]/someterms (if a state is selected)
These are already defined in my routes.
I was initially going to add some logic on the controller to determine the appropriate URL structure, but I was thinking to simply add that as an additional field in the geo table, so it would be a property of the geo collection.
Here is my jQuery function to display the collection details when, fired on keypress in the textbox:
$(function () {
$("#txtGeoLocation").autocomplete(txtGeoLocation, {
source: function (request, response) {
$.ajax({
url: "/home/FindLocations", type: "POST",
dataType: "json",
selectFirst: true,
autoFill: true,
mustMatch: true,
data: { searchText: request.term, maxResults: 10 },
success: function (data) {
response($.map(data, function (item) {
return { label: item.GeoDisplay, value: item.GeoDisplay, id: item.GeoID }
}))
}
})
},
select: function (event, ui) {
alert(ui.item ? ("You picked '" + ui.item.label + "' with an ID of " + ui.item.id)
: "Nothing selected, input was " + this.value);
document.getElementById("hidLocation").value = ui.item.id;
}
});
});
What I would like is to have structure the URL based on an object parameter (seems the simplest). I can only seem to read the parameters on "selected", and not on button click.
How can I accomplish this?
Thanks.
To resolve this, I removed the select: portion from the Javascript, and added the selected object parameters in the MVC route sent to my controller.