Skip to main content

Search and Top Navigation

#8221 closed enhancement (notabug)

Opened March 26, 2012 10:48PM UTC

Closed March 27, 2012 02:46AM UTC

Last modified March 27, 2012 12:26PM UTC

Pass function for options.source

Reported by: dcarbone Owned by:
Priority: minor Milestone: 1.9.0
Component: ui.autocomplete Version: 1.8.18
Keywords: Cc:
Blocked by: Blocking:
Description

Hey all,

I have a form that I wanted to have multiple autocomplete fields on thus necessitating $("#blah, #blah2").autocomplete();, etc.

The issue is the URL I wish to use in the ajax call is different depending on which input box the user inputs data into.

The current methods which need to be changed are as follows:

_initSource: function() {
		var self = this,
			array,
			url;
		if ( $.isArray(this.options.source) ) {
			array = this.options.source;
			this.source = function( request, response ) {
				response( $.ui.autocomplete.filter(array, request.term) );
			};
		} else if ( typeof this.options.source === "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",
					context: {
						autocompleteRequest: ++requestIndex
					},
					success: function( data, status ) {
						if ( this.autocompleteRequest === requestIndex ) {
							response( data );
						}
					},
					error: function() {
						if ( this.autocompleteRequest === requestIndex ) {
							response( [] );
						}
					}
				});
			};
		} else {
			this.source = this.options.source;
		}
	},

and

_search: function( value ) {
		this.pending++;
		this.element.addClass( "ui-autocomplete-loading" );

		this.source( { term: value }, this.response );
	},

I propose a change to below:

_initSource: function() {
		var self = this,
			array,
			url;
		
		if ( $.isArray(this.options.source) ) {
			array = this.options.source;
			this.source = function( request, response ) {
				response( $.ui.autocomplete.filter(array, request.term) );
			};
		} else if ( typeof this.options.source === "string" || typeof this.options.source === "function" ) {
			if (typeof this.options.source === "string") {
				url = this.options.source;
			}
			this.source = function( request, response ) {
				if (typeof this.options.source === "function") {
					url = this.options.source(request);
					request = {term : request.term };
				}
				if ( self.xhr ) {
					self.xhr.abort();
				}
				self.xhr = $.ajax({
					url: url,
					data: request,
					dataType: "json",
					context: {
						autocompleteRequest: ++requestIndex
					},
					success: function( data, status ) {
						if ( this.autocompleteRequest === requestIndex ) {
							response( data );
						}
					},
					error: function() {
						if ( this.autocompleteRequest === requestIndex ) {
							response( [] );
						}
					}
				});
			};
		} else {
			this.source = this.options.source;
		}
	},

and

_search: function( value ) {
		this.pending++;
		this.element.addClass( "ui-autocomplete-loading" );

		this.source( { term: value , currentTarget: this.element }, this.response );
	},

The main differences being that in the event that this.options.source is a function, that function is called each time by this.search and the modified parameters it passes to allow an element-specific determination of the URL.

This is a very rough hack I have done, but I believe the logic is sound. There might be additional methods which need to be changed to accommodate it, as this is a very rough implementation.

Attachments (0)
Change History (4)

Changed March 26, 2012 10:53PM UTC by dcarbone comment:1

a slight modification to _sourceInit:

if (typeof this.options.source === "function") {
    url = this.options.source(request);	
}
request = {term : request.term };

Either way, the request variable will need to be redefined in this case.

Changed March 27, 2012 02:46AM UTC by scottgonzalez comment:2

resolution: → invalid
status: newclosed

First of all, you can pass a function as the source. Second of all, you should just initialize your instances properly. If you need different settings for different elements, then initialize them separately, each with the proper settings.

Changed March 27, 2012 03:13AM UTC by dcarbone comment:3

Why? It makes no sense to have every autocomplete field initialized separately. There are even other jQuery UI plugins that take full advantage of passing in multiple elements and setting up different functionality between the two. The calendar is a prime example of this. Why should this plugin require you to have 90% redundant code when initiating more than 1 autocomplete field? Makes no sense to me.

Also, you can pass a function in but it is sort of pointless when using more than 1 autocomplete field as the object passed to that function would ONLY contain {term : "string"}, not the source element. Which, again, is fine if you only setup a single autocomplete field, but with multiple you need to know which element is triggering the method.

It seems very static to me.

Changed March 27, 2012 12:26PM UTC by scottgonzalez comment:4

I'm sorry you feel that way, that's just how it is. You'll need to make a better case than "it makes no sense" when arguing against a design that's widely accepted and used pretty much everywhere.