Loading pages of ajax select2 data in the background - jquery-select2

I have a AJAX select2 drop-down menu set up to do infinite paging. What'd I'd like to do is go ahead and load the first page of results in the background so that as soon as the user clicks the drop-down, they have a set of options immediately, instead of waiting on the initial AJAX call.
When I search Google for how to do this, I only see results about trying to set an initial selection, which isn't what I want to do. I just want to pre-load the first page of results from my endpoint so the user sees data immediately instead on waiting for the AJAX call to return. Is this even possible?
My select2 setup code is below:
$(".my-class-identifier").select2({
ajax: {
cache: true,
dataType: "json",
delay: 500,
data: function(params, page) {
return {
name: params,
otherParams: $(this).data("other-params")
page: page
};
},
results: function(data, page) {
return {
/* The server returns no data after all the pages have been returned. */
more: data && data.length > 0,
results: data
};
},
type: "GET",
url: function() {
if ($(this).data("url")) {
return $(this).data("url");
} else {
return DEFAULT_ENDPOINT;
}
}
},
allowClear: true,
minimumInputLength: 0,
placeholder: "Search for some data..."
});

So what you are looking for is to initialize your select2. Select2 has an initialize option to load your values, it would look something like this:
initSelection: function(element, callback){
callback([
{id:1,text:'one'},
{id:2,text:'two'},
]);
}
Be sure to add this outside your ajax call, but inside your select2.
An example here in JSFiddle

How about in your backend, you check your search string. If the size is zero or the search string is empty, then send back your first-page result. Otherwise, do the search and return the result.

Related

Select2 infinite scroll with no limited result

I am using selct2 and I am loading some data with ajax. The result is not limited, but I have to make infinite scroll. Some ideas?
I have a json like this
{"len":30,"data":[{"value":"223118","type":1,"name":"Peter","language":3},
{"value":"223118","type":1,"name":"John","language":2},
{"value":"223118","type":2,"name":"Mike","language":1},
{"value":"223118","type":1,"name":"George","language":3}
....
]}
And I am using the standart select2 functionality for ajac loading:
$( selector ).select2({
placeholder: "Search",
minimumInputLength: 1,
id: function(bond){return {id: bond._id};},
ajax: {
//url: $("#area-of-operating_0").attr('data-url'),
url: 'myurl',
dataType: 'json',
quietMillis: 100,
data: function (term, page) {
return {
page_limit: 10, // page size
page: page // page number
//q: term
};
},
results: function (data, page) {
var more = (page * 10) < data.total;
return {results: data.data, more: more}
}
},
formatResult: selectFormatResult, // see example
formatSelection: selectFormatSelection
})
The problem is that the json can be with 1000 elements. I want to make infinite scroll with 10 elements
This is not possible using just select2 as the mentioned in the demo
In order to enable the remote service must support some sort of a paging mechanism...
it is not possible to enable infinite scrolling if the server side does not support paging.
What you could do is to save the whole JSON in some JS-variable at the first request. Then you can sequentially get some more results out of the local variable when the user is scrolling down. Is this an option for you?

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 spinner after ajax call

I'm trying to use the jquery ui spinner on forms dynamically inserted through ajax calls.
To handle ajax calls I'm relying on ajaxy.
On success I call this function like so:
response: function(){
var Ajaxy = $.Ajaxy; var data = this.State.Response.data; var state = this.state;
var State = this.State;
var Action = this;
Action.documentReady($content);
updater(); // THE FUNCTION TO BIND NEW ELEMENTS
return true;
},
Here's the function
function updater(){
$('.spin').spinner();
}
And this works without any problem. But then When I call that same function on "normal" jquery requests (not ajaxy ones), it doesn't work anymore:
$.ajax({
type: "GET",
url: url,
cache: false,
dataType:"json",
success: function(res) {
updateTarget(res,target,animation);
updater();
}
}
});
I really don't see why in one case it is working, while in the other it isn't...
I've figured it out... My error was that I was running updater() after updateTarget(res,target,animation);, which is the function that analyses the json response and attach html elements to the page, while I must run updater() inside updateTarget(res,target,animation); right after the attachment to the page through .html().

Pass data to a html page opened in jQuery UI dialog

I have succeeded in opening a html page in a jQuery dialogbox. I have also passed a data called 'vendorid' to it, but do not know how to retrieve it in the html page that opens.
$('#btnShowSupplierStats').click(function () {
showUrlInDialog('../supplierstats.htm?vendorId=' + $(this).attr('vendorId')); return false;
//showUrlInDialog('../Default2.aspx'); return false;
});
function showUrlInDialog(url) {
var vid = $(this).attr('vendorId')
var tag = $("<div id='statsDiv' vid ='"+ vid +"'></div>");
$.ajax({
url: url,
success: function (data) {
tag.html(data).dialog({ show: "fadein", hide: "fadeout",
modal: true, minHeight: 550, minWidth: 800, autoOpen: false,
close: function (event, ui) { $(this).remove(); }, buttons: [{
text: "Close", click: function () { $(this).dialog('close');
return false; } }] }).data("vendorid", vid).dialog('open');
return false;
}
});
}
UPDATE: The answer from Farrukh would do this job ( i.e. second option he mentioned, since I am using a 100% client-side approach). But I did not store the data to be passed in a hiddent field. Instead I used the following approach which also works always:
When you are about to open the dialog through jQuery, just before this, set the attribute for the button which is the opener of dialog. In my case, I set an attribute called 'vendorid' on this button. Let's say the id of the opener button is 'btnOpenVendorDialog'. So need to use the following code.
$('#btnOpenVendorDialog').attr('vendorid', 'Vendor1234');
//open your dialog here ....
2.Then, in the html page that opens in dialog window, I can easily retrieve this attribute without any problems.
var vid = $('#btnOpenVendorDialog').attr('vendorid');
THAT's IT. YOU ARE ALL READY TO do whatever you want with this passed data.
There are two ways to do this:
Use server side and get the passed data in post or get(in your case vendor ID is get) and use it to populate a hidden element or data-vendorID attribute of any available element.
If you are using javascript then data vendorID that you have passed as vid is available after success function so place it in as a hidden input field or place it as a data-vendor-id on one of the other DOM elements and access it from there.

jQuery UI- How to prevent autocomplete menu from temporarily disappearing between keystrokes?

I'm using jQuery UI autocomplete with data from a remote datasource. My use case is really similar to the example here:
http://jqueryui.com/demos/autocomplete/#remote
The only difference is that I set my delay to 0. In between the keystrokes, the menu disappears for about 1/10th of a second ~100milli seconds prior to the updated autocomplete list being displayed.
Is there anyway I can prevent the menu from temporarily disappearing between keystrokes? A good use case is google's search, where between keystrokes, the suggestion box does not temporarily disappear.
IMO, it is not a good practice to set a delay of zero when using a remote datasource. It will send more requests than needed and surcharge the server with no benefit.
Anyway, I think you can achieve what you want by defining the source option as a callback yourself.
First a bit of explanaton. I suppose you are using the remote feature passing an url as the source for the plugin. The plugin actually wraps this into a callback implemented this way:
// in case the option "source" is a string
url = this.options.source;
this.source = function(request, response) {
if (self.xhr) {
self.xhr.abort();
}
self.xhr = $.ajax({
url: url,
data: request,
dataType: "json",
autocompleteRequest: ++requestIndex,
success: function(data, status) {
if (this.autocompleteRequest === requestIndex) {
response(data);
}
},
error: function() {
if (this.autocompleteRequest === requestIndex) {
response([]);
}
}
});
};
As you can see, if there is already an ajax request going on, it abords it. This happenning in your case as a request, as fast as your server can be, takes some time and your delay is zero.
if (self.xhr) {
self.xhr.abort();
}
This will actually execute the error callback of the aborted request that will execute itself the response callback with an empty dataset. If you look at the response callback, it closes the menu if data is empty:
_response: function(content) {
if (!this.options.disabled && content && content.length) {
...
} else {
this.close();
}
You can actually define your own source callback to make your ajax request yourself and change the default behavior by not aborting any pending request. Something like:
$('#autocomplete').autocomplete({
source: function(request, response) {
$.ajax({
url: url,
data: request,
dataType: "json",
success: function(data, status) {
// display menu with received dataset
response(data);
},
error: function() {
// close the menu on error by executing the response
// callback with an empty dataset
response([]);
}
});
}
});

Resources