Can someone help me understand the following code? I found it here.
It takes advantage of the JQuery UI Autocomplete with a remote source. I've commented the code as best I can and a more precise question follows it.
$( "#city" ).autocomplete({
source: function( request, response ) {
//request is an objet which contains the user input so far
// response is a callback expecting an argument with the values to autocomplete with
$.ajax({
url: "http://ws.geonames.org/searchJSON", //where is script located
dataType: "jsonp", //type of data we send the script
data: { //what data do we send the script
featureClass: "P",
style: "full",
maxRows: 12,
name_startsWith: request.term
},
success: function( data ) { //CONFUSED!
response(
$.map(
data.geonames, function( item ) {
return {
label: item.name+(item.adminName1 ? ","+item.adminName1:"")+","+item.countryName,
value: item.name
}
}
)
);
}
});
}
});
As you can see, I don't understand the use of the success function and the response callback.
I know the success function literal is an AJAX option which is called when the AJAX query returns. In this case, it seems to encapsulate a call to the response callback? Which is defined where? I thought by definition of a callback, it should be called on its own?
Thanks!
The response object as defined by the documentation ("Overview" page):
A response callback, which expects a
single argument to contain the data to
suggest to the user. This data should
be filtered based on the provided
term, and can be in any of the formats
described above for simple local data
(String-Array or Object-Array with
label/value/both properties). It's
important when providing a custom
source callback to handle errors
during the request. You must always
call the response callback even if you
encounter an error. This ensures that
the widget always has the correct
state.
so, the 'response' argument is actually a callback, which must be called upon success of the ajax retrieval of autocomplete items.
Since your data will come back via AJAX, your code must update the widget manually. jQueryUI provides an argument as a function so that your code can do that update by calling the function.
You can see the response object defined in the declaration of the function used for the source option:
source: function( request, response )
You could even take the AJAX call out of the equation and do something like this:
source: function(request, response) {
response([{label:'foo', value: 'foo'},{label:'bar', value:'bar'}]);
}
Would immediately call the response callback with an array of label/value pairs for the widget.
Related
I'm trying to use the Autocomplete plugin to produce a search box similar to the one at IMDB, I want it to:
Show more than just text in the options.
To act like a link when an option is selected (as each option will be a unique record).
That, if the user chooses, they press the button and a search is performed on their input.
The docs say that there are 3 types of datasource it will work with:
A data source can be:
an Array with local data
a String, specifying a URL
a Callback
I can get the autocomplete to work with the 2nd option, but then there isn't the ability to transform the data returned, it just takes the JSON and puts it straight in the dropdown. This means the source url can only return data in the format {label: "blah", value: "blurg"}.
If I could inject a transformation function, then I could have the url return whatever JSON I like, the function would change the data into the format the autocomplete expects but also formatted as I wish, and all without changing the response served by the url (I'm not going to return HTML from there, only JSON).
e.g. The url could return this:
{ label:"Grosse Point Blank", id: 3, img:"/imgs/gpb.png",...}
and a transform function could mung it into something like this:
{ label:"<a href='/films/3/grosse-point-blank'><img src='/imgs/gpb.png' />Grosse Point Blank</a>", value="grosse-point-blank"}
I've tried using option 3, a callback, with a getJSON call, but I can't get it to work. The nearest code I've found to what I may need is here, but it has options that aren't listed in the current docs for Autocomplete and I don't understand how to use the response object.
Is there an example of using the callback method with an AJAX request, or how I can inject a function that transforms the code?
You can use the _renderItem() private method of autocomplete to format the return data. It looks like this:
$("#element").autocomplete({...}).data( "autocomplete" )._renderItem = function( ul, item ) {
//...
};
You can see the method definition here https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.autocomplete.js#L520
An example of code that I have used:
$("#element").autocomplete({...})
.data("autocomplete")._renderItem = function( ul, item ) {
return $( "<li></li>" ) // create the list item
.data( "item.autocomplete", item ) // save the data to the DOM
.append( "<a>"+ item.label + "</a>" ) // add the HTML
.appendTo( ul ); // append to the UL
};
Here's a brief structure when using callback method.
source: function( request, response ) {
$.ajax({
...
success: function( data ) {
// do transformation of data here
response(data);
}
});
}
Quoted from your link, http://api.jqueryui.com/autocomplete/#option-source
The third variation, the callback, provides the most flexibility, and
can be used to connect any data source to Autocomplete. The callback
gets two arguments:
A request object, with a single property called "term", which refers
to the value currently in the text input. For example, when the user
entered "new yo" in a city field, the Autocomplete term will equal
"new yo".
A response callback, which expects a single argument to
contain the data to suggest to the user. This data should be filtered
based on the provided term, and can be in any of the formats described
above for simple local data (String-Array or Object-Array with
label/value/both properties). It's important when providing a custom
source callback to handle errors during the request. You must always
call the response callback even if you encounter an error. This
ensures that the widget always has the correct state.
I've got a Google Searchbar-type input field. When I type in a couple characters and wait for half a second it runs the ajax call to an external website I've set in the "source" function of the autocomplete code and once it has returned the results it returns it to the screen (like it should).
The problem is that while the ajax call is being run to fetch the results it won't allow me to continue typing in the input field until the ajax call has completed.
How can I get it to allow me to continue typing while the ajax call is being made?
Here is my jQuery function:
$('#googleSearchbar').autocomplete({
minLength: 2,
autoFocus: true,
delay: 500,
source: function (request, response) {
results = $.parseJSON($(this).callJson('post', 'http://my_external_url', {
data: request.term
}));
response(results);
},
error: function (err) {
console.error('ERROR : ' + err);
return false;
}
});
I have a hunch you are blocking the browser when making your AJAX request. This line:
results = $.parseJSON($(this).callJson('post', 'http://my_external_url', {
data: request.term
}));
Makes me think that $(this).callJson(...) is a synchronous request, which is going to lock up the entire browser for the duration of the request.
You need to make an asynchronous request and call the response function when that request completes. This should stop the browser from locking up.
I have an Ajax form with a data-remote="true" attribute which I'm submitting to a controller in rails.
What I'd like to do is to use the jquery-ujs event system to append data to the request before it gets sent to the server.
Something like this:
$("#my_form").bind('ajax:beforeSend', function(xhr, settings){
// serialize some object and append it
});
I can't figure out how to actually tag on the data though?
Edit This is what the 'xhr' object looks like in the console.
f.Event
currentTarget: DOMWindow
data: undefined
exclusive: undefined
handleObj: Object
handler: function N(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(y,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}
jQuery16103879226385615766: true
liveFired: HTMLDocument
namespace: ""
namespace_re: /(^|\.)(\.|$)/
result: undefined
target: HTMLFormElement
timeStamp: 1306788242911
type: "ajax:beforeSend"
__proto__: Object
I figured this one out myself in the end. It appears that despite what the docs say, the ajax:beforeSend hook actually takes three arguments. According to this helpful blog post they are event, xhr and settingsin that order.
The form data I was looking for is in the in the data attribute of the settings argument.
So basically, I can now add data to the request with the function
$("#my_form").bind('ajax:beforeSend', function(event, xhr, settings){
settings.data += "&serialized=data";
});
And the extra paramaters will be available on the server.
As of the jQuery 1.5, there is a local beforeSend callback that can be bound to your request. Documentation link and another documentation link
EDIT: The request data is available in the jqXHR object, the first parameter beforeSend callback.
beforeSend(jqXHR, settings)
Try inspecting the jqXHR object in firebug to see exactly where it stores the POST data and just append your values,
To change data attribute (at least now, using Jquery 1.7.2) inside the beforeSend method (the Jquery native method), you can just alter the this.data variable.
I am using JQuery UI plugin blockUI to block UI for every ajax request. It works like a charm, however, I don't want to block the UI (Or at least not show the "Please wait" message) when I am making ajax calls to fetch autocomplete suggest items. How do I do that? I am using jquery autocomplete plugin for autocomplete functionality.
Is there a way I can tell the block UI plug-in to not block UI for autocomplete?
$('#myWidget').autocomplete({
source: function(data, callback) {
$.ajax({
global: false, // <-- this is the key!
url: 'http:...',
dataType: 'json',
data: data,
success: callback
});
}
});
Hm, looks to be a missing feature in jquery :)
You could use a global flag to indicate if it is a autocomplete call and wrap it in a general autcompletefunction
var isAutoComplete = false;
function autoComplete(autocomplete){
isAutoComplete = true;
if($(autocomplete).isfunction())
autocomplete();
}
$(document).ajaxStart(function(){if(!isAutoComplete)$.blockUI();}).ajaxStop(function(){isAutoComplete = false;$.unblockUI();});
It's not a nice solution but it should work...
try using a decorator
$.blockUI = function() {
if (condition_you_dont_want_to_block) {
return;
}
return $.blockUI.apply(this, arguments);
}
or you can write your own block function that is smarter
function my_blockUI() {
if (condition_you_dont_want_to_block) {
return;
}
$.blockUI();
}
$(document).ajaxStart(my_blockUI).ajaxStop($.unblockUI);
You can set blockUI to work for all functions on the page by adding to a global jQuery event handler. To make sure it doesn't get called on autocomplete ajax calls we have to determine if the call is an autocomplete call or not. The problem is that these global functions don't have that much information available to them. However ajaxSend does get some information. It gets the settings object used to make the ajax call. the settings object has the data string being sent. Therefore what you can do is append to every data string in every ajax request on your page something like:
¬autocomplete=notautocomplete
For example:
$.ajax({data:"bar=1&foo=2¬autocomplete=notautocomplete"})
Then we can put this code in your document ready section before anything else:
$(document).ajaxSend(
function (event, xhr, ajaxOptions){
if(ajaxOptions.data.indexOf("notautocomplete") !== -1){
$.blockUI;
}
});
$(document).ajaxStop($.unblockUI);
Of course the other better idea would be to look for something unique in the auto complete requests, like the url, but that depends on which autocomplete plug-in you are using and how you are using it.
using a modal block (block UI) means blocking any inputs from user, I'd suggest plain old throbber to show 'Please wait..' and to block ( set attributes readonly="readonly" ) ur input controls till the ajax request is complete.
The above UI seems to be self conflicting!
I am calling an ASP.NET MVC action
public JsonResult GetPatient(string patientID)
{
...
from JavaScript using jQuery. The following call works
$.getJSON(
'/Services/GetPatient',
{ patientID: "1" },
function(jsonData) {
alert(jsonData);
});
whereas this one does not.
$.ajax({
type: 'POST',
url: '/Services/GetPatient',
data: { patientID: "1" },
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function(jsonData) {
alert(jsonData);
},
error: function() {
alert('Error loading PatientID=' + id);
}
});
Both reach the action method, but the patientID value is null w/ the $.ajax call. I'd like to use the $.ajax call for some of the advanced callbacks.
Any thoughts appreciated.
Content-type
You don't need to specify that content-type on calls to MVC controller actions. The special "application/json; charset=utf-8" content-type is only necessary when calling ASP.NET AJAX "ScriptServices" and page methods. jQuery's default contentType of "application/x-www-form-urlencoded" is appropriate for requesting an MVC controller action.
More about that content-type here: JSON Hijacking and How ASP.NET AJAX 1.0 Avoids these Attacks
Data
The data is correct as you have it. By passing jQuery a JSON object, as you have, it will be serialized as patientID=1 in the POST data. This standard form is how MVC expects the parameters.
You only have to enclose the parameters in quotes like "{ 'patientID' : 1 }" when you're using ASP.NET AJAX services. They expect a single string representing a JSON object to be parsed out, rather than the individual variables in the POST data.
JSON
It's not a problem in this specific case, but it's a good idea to get in the habit of quoting any string keys or values in your JSON object. If you inadvertently use a JavaScript reserved keyword as a key or value in the object, without quoting it, you'll run into a confusing-to-debug problem.
Conversely, you don't have to quote numeric or boolean values. It's always safe to use them directly in the object.
So, assuming you do want to POST instead of GET, your $.ajax() call might look like this:
$.ajax({
type: 'POST',
url: '/Services/GetPatient',
data: { 'patientID' : 1 },
dataType: 'json',
success: function(jsonData) {
alert(jsonData);
},
error: function() {
alert('Error loading PatientID=' + id);
}
});
.getJson is simply a wrapper around .ajax but it provides a simpler method signature as some of the settings are defaulted e.g dataType to json, type to get etc
N.B .load, .get and .post are also simple wrappers around the .ajax method.
Replace
data: { patientID: "1" },
with
data: "{ 'patientID': '1' }",
Further reading: 3 mistakes to avoid when using jQuery with ASP.NET
There is lots of confusion in some of the function of jquery like $.ajax, $.get, $.post, $.getScript, $.getJSON that what is the difference among them which is the best, which is the fast, which to use and when so below is the description of them to make them clear and to get rid of this type of confusions.
$.getJSON() function is a shorthand Ajax function (internally use $.get() with data type script), which is equivalent to below expression, Uses some limited criteria like Request type is GET and data Type is json.
Read More .. jquery-post-vs-get-vs-ajax
The only difference I see is that getJSON performs a GET request instead of a POST.
contentType: 'application/json; charset=utf-8'
Is not good. At least it doesnt work for me. The other syntax is ok. The parameter you supply is in the right format.
with $.getJSON()) there is no any error callback only you can track succeed callback and there no standard setting supported like beforeSend, statusCode, mimeType etc, if you want it use $.ajax().