Select2 with remote datasource dropdown show "searching... - undefined" and then "The results could not be loaded" after select - jquery-select2

I use select2 4.0.5 with ajax. It work well if I search and select result on dropdown.
But after select one value on dropdown, if I reopen dropdown again (click on select2 object) dropdown show "searching... - undefined" and then "The results could not be loaded"

I have fixed it.
"searching... - undefined" : on templateResult i add check result.loading
templateResult : function (result) {
if (result.loading)
return "Searching...";
return result.text + " - " + result.description;
}
On dropdown reopen after select, I found ajax data params.term is blank when dropdown reopen. It fix by
ajax: {
...
data: function (params) {
var query = {
term: params.term || $('#CurrencyID').select2('data')[0].text
}
}

Related

How to dynamically bind options to a select2 element

I have a select2 dropdown which has to get its options dynamically as user types the letters.
HTML:
<select class="select2 select2-hidden-accessible ddlSegments" multiple=""></select>
Here is the function that triggers for each letter that user types.
JS:
function GetSegmentsByKeyword(ddlSegments, keyword) {
$(ddlSegments).html("").trigger("change");
$.ajax({
type: "POST",
url: "/Common/GetSegmentsByKeyword",
data: "{'keyword': " + JSON.stringify(keyword) + "}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
if (response.Segments.length > 0) {
//bind data
var options = "";
var jsonObject = $.parseJSON(response.Segments);
$.each(jsonObject, function (i, obj) {
options += '<option value="' + obj.SegmentValue + '">' + obj.DisplayName + '</option>';
});
$(ddlSegments).append(options);
}
}
});
}
Here is the problem I experience:
When user entered say '12', the relevant data from table is returned and bound to the options. But the popup list doesn't show up. It shows up only after user types another letter.
i.e, when user types '12', the correct data is bound and doesn't show the list. But when user types '123', the list shows up. However, the data in the list is only related to '12'. When user types '1234', the list shows up with data related to '123'.
What should be done, so that the select2 list shows up dynamically right after binding the data.
I guess you missed to trigger event on key up in select2 like this for example
$(document).on('keyup', '.ddlSegments', function (e),
please check the fiddle here: Updated Fiddle
Check out the minimumSearchLength configuration parameter. Here is the documentation.
$('select').select2({
minimumInputLength: 3 // only start searching when the user has input 3 or more characters
});

Select2 with createSearchChoice uses newly created choice for keyboard entry even given a match, bug or am I missing something?

I'm using Select2 (version 3.4.0) to populate a tag list. The tags are matched against existing ones via ajax call, and I'm using createSearchChoice to allow creating new tags. The code works so far, and looks something like this:
$(mytags).select2({
multiple: true,
placeholder: "Please enter tags",
tokenSeparators: [ "," ],
ajax: {
multiple: true,
url: myurl,
dataType: "json",
data: function(term, page) {
return {
q: term
};
},
results: function(data, page) {
return data;
}
},
createSearchChoice: function(term) {
return {
id: term,
text: term + ' (new)'
};
},
});
All pretty standard, except note the appended (new) in createSearchChoice. I need users to know that this is not a preexisting tag.
It works as expected: if I start typing "new-tag", I get "new-tag (new)" tag suggested at the top of the list, and if I pick it, the tag list contains "new-tag (new)", as expected. If the tag already exists, Select2 detects the match, and no "(new)" choice is created. Pressing return or clicking on the match works as expected.
The problem appears when I type a comma (my single tokenSeparators entry) while there is a match. Select2 closes that token, and adds the tag to the list, but with the "(new)" label appended, i.e. it uses the return value from createSeachChoice even if it does not have to.
Is this a bug in Select2, or am I using it wrong (and what should I do instead)?
I 'm not sure if this is a bug or not -- in any case, there is no open issue referring to this behavior at the GitHub issue tracker at this moment.
You can mostly fix the behavior yourself though. The idea is that the createSearchChoice callback must be able to tell if term refers to a search result or not. But createSearchChoice does not have direct access to the search results, so how can we enable that? Well, by saving the latest batch of search results from inside the results callback.
var lastResults = [];
$(...).select2({
ajax: {
multiple: true,
url: "/echo/json/",
dataType: "json",
type: "POST",
data: function (term, page) {
return {
json: JSON.stringify({results: [{id: "foo", text:"foo"},{id:"bar", text:"bar"}]}),
q: term
};
},
results: function (data, page) {
lastResults = data.results;
return data;
}
},
createSearchChoice: function (term) {
if(lastResults.some(function(r) { return r.text == term })) {
return { id: term, text: term };
}
else {
return { id: term, text: term + " (new)" };
}
}
});
This code uses Array.some so you need something better than IE8 (which is the select2 minimum requirement) to run it, but of course it is possible to emulate the behavior.
See it in action.
There is, however, a fly in the ointment: this code works correctly only if the search results corresponding to the current search term have been already received.
This should be obvious: if you type very fast and create a search term that corresponds to an existing tag but hit comma before the search results that include that tag have arrived, createSearchChoice will be testing for the tag's presence among the previously received search results. If those results do not include the tag, then the tag will be displayed as "new" even though it is not.
Unfortunately I don't believe there is anything you can do to prevent this from happening.
Instead of tweeking the result, I think it is better to work on the server side.
If the server doesn't find a tag make it return a json answer with the new tag
{"more":false,"results":[{"id":"whatever","text":"new-tag (new)"}]}
There is another parameter for the 'createSearchChoice' - 'page', it lists all the choices, you can easily find dupes with it.
createSearchChoice = function (term, page) {
if( page.some(function(item) {
return item.text.toLowerCase() === term.toLowerCase();
}) ){
return { val: term, name: term + '*' };
}
}

jquery-ui tabs - adding tabs

I have a list of items shown in one tab and I want to be able to click on one of the items and open a new tab and show its details on the new tab. I am adding the new tab by doing the following:
$(".btn-opener").click(function(){
$('#tabs').tabs('add', 'http://localhost/GetItem/' + $(this).attr('href'), "View Details");
}
My problem is that the url is actually a service endpoint returning JSON. I want to be able to apply a js template to the returned data. How do I do this? Do I need to hook into the tab's load event?
I think I'm going to solve this by adding a div with the HTML I need into the tab conainer, and then using its id to set the fragment of the new tab. For example:
$('.btn-opener').click(function () {
var $this = $(this),
itemID = $this.data("item-id");
$.get("http://localhost/GetItem/" + itemID,
function (data, textStatus, jqXHR) {
var html = Mustache.to_html($("#item-template").html(), data);
$("#tabs").append('<div id="' + id + '">' + html + '</div>');
$('#tabs').tabs("add", "#" + id, $this.text());
},
"json"
);
return false;
});
I will wait to see if anyone comes up with a better solution before marking this as the answer.

Lift - Autocomplete with Ajax Submission

I would like to use an autocomplete with ajax. So my goal is to have:
When the user types something in the text field, some suggestions provided by the server appear (I have to find suggestions in a database)
When the user presses "enter", clicks somewhere else than in the autocomplete box, or when he/she selects a suggestion, the string in the textfield is sent to the server.
I first tried to use the autocomplete widget provided by lift but I faced three problems:
it is meant to be an extended select, that is to say you can originally only submit suggested values.
it is not meant to be used with ajax.
it gets buggy when combined with WiringUI.
So, my question is: How can I combine jquery autocomplete and interact with the server in lift. I think I should use some callbacks but I don't master them.
Thanks in advance.
UPDATE Here is a first implementation I tried but the callback doesn't work:
private def update_source(current: String, limit: Int) = {
val results = if (current.length == 0) Nil else /* generate list of results */
new JsCmd{def toJsCmd = if(results.nonEmpty) results.mkString("[\"", "\", \"", "\"]") else "[]" }
}
def render = {
val id = "my-autocomplete"
val cb = SHtml.ajaxCall(JsRaw("request"), update_source(_, 4))
val script = Script(new JsCmd{
def toJsCmd = "$(function() {"+
"$(\"#"+id+"\").autocomplete({ "+
"autocomplete: on, "+
"source: function(request, response) {"+
"response("+cb._2.toJsCmd + ");" +
"}"+
"})});"
})
<head><script charset="utf-8"> {script} </script></head> ++
<span id={id}> {SHtml.ajaxText(init, s=>{ /*set cell to value s*/; Noop}) } </span>
}
So my idea was:
to get the selected result via an SHtml.ajaxText field which would be wraped into an autocomplete field
to update the autocomplete suggestions using a javascript function
Here's what you need to do.
1) Make sure you are using Lift 2.5-SNAPSHOT (this is doable in earlier versions, but it's more difficult)
2) In the snippet you use to render the page, use SHtml.ajaxCall (in particular, you probably want this version: https://github.com/lift/framework/blob/master/web/webkit/src/main/scala/net/liftweb/http/SHtml.scala#L170) which will allow you to register a server side function that accepts your search term and return a JSON response containing the completions. You will also register some action to occur on the JSON response with the JsContext.
3) The ajaxCall above will return a JsExp object which will result in the ajax request when it's invoked. Embed it within a javascript function on the page using your snippet.
4) Wire them up with some client side JS.
Update - Some code to help you out. It can definitely be done more succinctly with Lift 2.5, but due to some inconsistencies in 2.4 I ended up rolling my own ajaxCall like function. S.fmapFunc registers the function on the server side and the function body makes a Lift ajax call from the client, then invokes the res function (which comes from jQuery autocomplete) on the JSON response.
My jQuery plugin to "activate" the text input
(function($) {
$.fn.initAssignment = function() {
return this.autocomplete({
autoFocus: true,
source: function(req, res) {
search(req.term, res);
},
select: function(event, ui) {
assign(ui.item.value, function(data){
eval(data);
});
event.preventDefault();
$(this).val("");
},
focus: function(event, ui) {
event.preventDefault();
}
});
}
})(jQuery);
My Scala code that results in the javascript search function:
def autoCompleteJs = JsRaw("""
function search(term, res) {
""" +
(S.fmapFunc(S.contextFuncBuilder(SFuncHolder({ terms: String =>
val _candidates =
if(terms != null && terms.trim() != "")
assigneeCandidates(terms)
else
Nil
JsonResponse(JArray(_candidates map { c => c.toJson }))
})))
({ name =>
"liftAjax.lift_ajaxHandler('" + name
})) +
"=' + encodeURIComponent(term), " +
"function(data){ res(data); }" +
", null, 'json');" +
"""
}
""")
Update 2 - To add the function above to your page, use a CssSelector transform similar to the one below. The >* means append to anything that already exists within the matched script element. I've got other functions I've defined on that page, and this adds the search function to them.
"script >*" #> autoCompleteJs
You can view source to verify that it exists on the page and can be called just like any other JS function.
With the help of Dave Whittaker, here is the solution I came with.
I had to change some behaviors to get:
the desired text (from autocomplete or not) in an ajaxText element
the possibility to have multiple autocomplete forms on same page
submit answer on ajaxText before blurring when something is selected in autocomplete suggestions.
Scala part
private def getSugggestions(current: String, limit: Int):List[String] = {
/* returns list of suggestions */
}
private def autoCompleteJs = AnonFunc("term, res",JsRaw(
(S.fmapFunc(S.contextFuncBuilder(SFuncHolder({ terms: String =>
val _candidates =
if(terms != null && terms.trim() != "")
getSugggestions(terms, 5)
else
Nil
JsonResponse(JArray(_candidates map { c => JString(c)/*.toJson*/ }))
})))
({ name =>
"liftAjax.lift_ajaxHandler('" + name
})) +
"=' + encodeURIComponent(term), " +
"function(data){ res(data); }" +
", null, 'json');"))
def xml = {
val id = "myId" //possibility to have multiple autocomplete fields on same page
Script(OnLoad(JsRaw("jQuery('#"+id+"').createAutocompleteField("+autoCompleteJs.toJsCmd+")"))) ++
SHtml.ajaxText(cell.get, s=>{ cell.set(s); SearchMenu.recomputeResults; Noop}, "id" -> id)
}
Script to insert into page header:
(function($) {
$.fn.createAutocompleteField = function(search) {
return this.autocomplete({
autoFocus: true,
source: function(req, res) {
search(req.term, res);
},
select: function(event, ui) {
$(this).val(ui.item.value);
$(this).blur();
},
focus: function(event, ui) {
event.preventDefault();
}
});
}
})(jQuery);
Note: I accepted Dave's answer, mine is just to provide a complete answer for my purpose

jquery ui autocomplete - open event (handling no results)

I wanted to use the open event to return "no results found" when ui == null but in firebug it looks like that even after the open event is fired the ui list is always empty even if there are results.
My source is an XML file
I'm attempting:
open: function(event,ui) {
if( ui.item == null ) {
console.log("no results");
}
}
Add to autocomplete.js:
if(retMatch.length == 0) {
var noResult = new Array();
noResult[0] = {
label: "No results found",
title: "No results found",
description: "Your search did not return any results. Please refine your search criteria. If you believe this message is in error please fill out a support case."
}
return noResult;
}
to:
$.extend

Resources