Opened 8 years ago

Closed 8 years ago

Last modified 8 years ago

#8221 closed enhancement (notabug)

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.

Change History (4)

comment:1 Changed 8 years ago by dcarbone

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.

comment:2 Changed 8 years ago by Scott González

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.

comment:3 Changed 8 years ago by dcarbone

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.

comment:4 Changed 8 years ago by Scott González

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.

Note: See TracTickets for help on using tickets.