Select2 search against current list of items - jquery-select2

Using Select2's search defaults, I see it runs ajax calls with the query terms of the characters I typed. It does not filter against the current list of items. I didn't see anything in the docs to control this behavior. Is it possible to change it?

For ajax'd data, Select2 doesn't have an option to filter against the current, cached result set, or a freshly fetched set. It always gets a new data set.
My solution was to write my own filter on the fresh data. It works but for every search term, it makes a new ajax call, which just returns the same data, every time.
processResults: function (data, query) {
let normalizedData = hj.gic.swapFieldDataConditioner('name', 'text', data.results);
if(query.term === undefined) {
return {results: normalizedData};
} else {
let term = new RegExp(query.term, 'gi'), matchedResults = [];
normalizedData.forEach(function (item) {
if(item.text.match(term)) {
matchedResults.push(item);
}
});
return {results: matchedResults};
}
}

Related

PagedList parameter too long

I have an MVC PagedList which works just fine. I am filtering that list and the filter predicate is sent to the client during roundtrips. I use unobtusive ajax replacing. My pager code looks as:
#Html.PagedListPager((IPagedList)Model.Items,
page => Url.Action("Filter",
new ClientSearch
{
Page = page,
PageSize = Model.PageSize,
Predicate = Model.Predicate
}),
PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(
new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "clients-list",
}))
The problem is, that the Predicate parameter is too long. And it should be. I get the following exception:
"The request filtering module is configured to deny a request where the query string is too long."
I do not want to alter the web.config in order to allow long parameters. I would like to pass the model in a POST header instead of query string parameter. Is it possible with PagedList?
Thanks in advance.
I still couldn't figure out whether PagedList supported posting large data, however I ended up with the following workaround.
I have a post method which posts the model to the controller function and replaces the partial view content with the results.
function postToPage(url, size, predicate, replace) {
var data = {
size: size,
predicate: predicate
};
$.ajax({
url: url,
data: data,
type: 'POST',
success: function (result) {
$('#' + replace).html(result);
}
});
}
I also have another function to replace the URLs in the pagination-container div and wire up the click event to call the post method. The click event stops event propagation, so the URL in the href attribute won't be used.
function replaceHrefs() {
$('div[class = pagination-container').find('a').each(function (index, value) {
var url = value.href.toString();
value.addEventListener('click', function (event) {
event.stopPropagation();
post(url);
});
value.href = '#';
});
I created a custom version of post method in order to generate the pagesize and predicate from the model.
function post(url) {
postToPage(url, #Model.PageSize, '#Model.Predicate', 'clients-list');
}
I had to wire up the URL replacing procedure to two places: when the document becomes ready and when the ajax call completes. These covered all the cases I needed.
$( document ).ajaxComplete(function() {
replaceHrefs();
});
$( document ).ready(function() {
replaceHrefs();
});
I hope it helps someone.

Using select2 with json data that doesn't have a field called "text" while avoiding copying the items and loosing standard behaviors

I'm using select2 and I want to set a custom field for the text property of the rendered items without
replacing standard behavior (marking and such)
pushin all my array into a new one with the text field on it
ps: i just want to a render many select2 items that doesn't have a text field
Basically if you see this jsbin you will see something like this
$("#e10_3").select2({
data:{ results: data, text: function(item) { return item.tag; } },
formatSelection: format,
formatResult: format
});
But if I delete the custom formatSelection and formatResult parameters of select2 I loose my hability to use a different field for text.
I suggest this approach
$("#e10_3").select2({
data:{
results: function (data) {
return {
results: $.map(data, function (item) {
return {
text: item.tag,
id: item.id
...
}
})
};
}
},
formatSelection: format,
formatResult: format
});
As previous answer this solution DOES create a new array, but it seems to be a better approach considering readability. Before passing data you should modify it. You can see this in official docs
var data = $.map(yourArrayData, function (obj) {
obj.text = obj.text || obj.name; // desired field
return obj;
});
The only other option is to prepare data with text property matched to desired property from the very beginning.
UPDATE (added an example)
$('your-select2-el').select2
data: $.map(yourArrayData, (obj) ->
obj.text = obj.your_custom_field_name # obj.title or obj.name etc.
obj
)
See docs in the link I provided before and this one

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 + '*' };
}
}

use ajax call in jquery autocomplete only if result is not found in locally built array

In my web app , I am showing rates of stocks.I am using jquery autocomplete to show options while entring stocks name. But I have built local copy of javascript array. I want to show the options from this local array , If search term is not found in local array then ajax call must be made to get the list from server side.
Thanks !!!
//Local array
var local_array=["option1","option2"];
//jqueryUI call of autocomplete function
$('#search_stock').autocomplete({
source:function(){
if(search term is found in local array)
{
show suggestion from local array.
}
else
{
make ajax call to show suggestions of stock names.
}
}
});
UPDATE
Here's the actual code
$(function() {
var cache = {'option1':'option1','option2':'option2'}, lastXhr;
$( "#stock_rates" ).autocomplete({
minLength: 2,
source: function( request, response ) {
var term = request.term;
if ( term in cache ) {
response( cache[ term ] );
return;
}
lastXhr = $.getJSON( "stock_rates.php", request, function( data, status, xhr ) {
cache[ term ] = data;
if ( xhr === lastXhr ) { response( data ); }
});
}
});
});
The example pages for jQuery UI autocomplete have an example of exactly this issue.
http://jqueryui.com/demos/autocomplete/#remote-with-cache. Click the 'View Source' link on that page to see the code for the example.
The key part is that 'source' takes arguments.
source: function(request, response){
You need to read request, either fetch the value from your cache, or do a request, and then call the response function and pass it the matched values.
Update
Your problem now is that the format that you are storing in your cache is wrong. The cache just stores data as it would be returned from your getJSON call, indexed by the search term. It is up to you do to do the prefix checking and such.
To continue the way you are trying now, you'll either need to populate the cache properly.
var cache = {
"o": ['option1', 'option2'],
"op": ['option1', 'option2'],
// ....
"option1": ['option1'],
"option2": ['option2']
};
Otherwise, you could store the data differently and put more logic in your 'source' function to do the prefix checking on a static array or something. That all really depends on the data you are caching though.
Use search event of autocomplete and check your condition in that event and based on that return true or false if you want to make a ajax call respectively.
Below is the sample code.
$('#search_stock').autocomplete({
search:function(event,ui){
if(search term is found in local array)
{
return false;
}
else
{
return true;
}
}
});

How to wait for all ajax queries to finish (and use combined result)

var display_message="";
$('input:checked').each(function(index) {
var profile_id=$(this).val();
$.ajax({
type: 'post',
url: 'myUrl',
data: data,
success: function(data) {
if(data=="ok")
display_message = display_message + data +", ";
}
});
});
alert(display_message);
alert(display_message);
if($.trim(display_message)!=""){
jAlert("Your birthdate already exits in "+display_message.substring(0, display_message.length - 2)+".", "Bdate");
return false;
}
in this code, i use two alert-box for display display_message variable value.
when i run successfully this code, in 1st alert-box i get blank value and second alert-box i get value which i needed, then it will go in if condition.
if i doesn't use alert box then it will always take null value in display_message variable and never enters into the if condition. so what i need to change to run this code without alert box?
You are making an asynchronous call via AJAX, but your code is executing synchronously. So it is returning before the AJAX call completes. The first alert box just gives the function time to catch up. You need to handle all this code in your success callback.
var display_message="";
$('input:checked').each(function(index) {
var profile_id=$(this).val();
$.ajax({
type: 'post',
url: 'myUrl',
data: data,
success: function(data) {
if(data=="ok")
display_message = display_message + data +", ";
if($.trim(display_message)!=""){
jAlert("Your birthdate already exits in "+display_message.substring(0, display_message.length - 2)+".", "Bdate");
return false;
}
});
});
You want all your ajax queries to finish and return results, right?
Then this is a synchronization problem.
I would suggest this approach (code is simplified for clarity).
var inputs_processed = -1;
var inputs_to_process = -1;
function queryData() {
inputs_to_process = $('input:checked').length;
$('input:checked').each(function() {
$.ajax({success: function(data) {
inputs_processed += 1;
// build up that message
}});
});
}
function displayResult() {
if (inputs_processed == inputs_to_process) {
// display result
} else {
// not all queries finished yet. Wait.
setTimeout(displayResult, 500);
}
}
queryData();
displayResult();
Basically, you know how many requests should be made and you don't display result until that number of requests returns.
Why your data is "data"? I cant see any variable called data is declared here. You should pass in the value you want to use as the parameter into the data options.
Edit: This is why u getting the null value. data is not initialize into anything. Only after the success function, your "data" will have the value since you declare the return value with the same name

Resources