0

This example of using jquery-ui autocomplete with a remote web service has a nice concise syntax:

$( "#birds" ).autocomplete({
source: "search.php",
minLength: 2
});

Here search.php returns an array as follows:

[ 
    { "id": "Passer domesticus", "label": "House Sparrow", "value": "House Sparrow" },
    ...
]

I want to use a WCF web service, but the same syntax doesn't work because the array returned is wrapped in a 'd' container object:

{"d":
    [ 
    { "id": "Passer domesticus", "label": "House Sparrow", "value": "House Sparrow" },
    ...
    ]
}

Of course I can get round this by writing code to look into the "d" container, something like the following (untested - could have typos):

$( "#birds" ).autocomplete({
minLength: 2
     source: function (request, response) {
            $.getJSON("search.svc/GetBirds", request, function (data, status, xhr) {
                if (status == "success") response(data.d);
                }
            }
});

Is this the best I can do or is there some more concise syntax?

Ideally I'd like to be able to specify "source" as a url and have it work with responses that are returned with or without the "d" container.

Joe
  • 122,218
  • 32
  • 205
  • 338

1 Answers1

1

In my mind you have two options.

The first is to create a helper function that will map the results for you. This is probably the best/easiest solution. Simple code:

$( "#birds" ).autocomplete({
minLength: 2
     source: function (request, response) {
            $.getJSON("search.svc/GetBirds", request, function (data, status, xhr) {
                if (status == "success") 
                   handleResponse(data); //you write this function
                }
            }
});

The second option is you can "monkeypatch" the AutoComplete plugin functions to override the default behavior.

So in your case you want to override the $.ui.autocomplete.prototype._initSource function. Fair warning here that you are basically overriding a core function in the UI library and if that library is ever updated your function will always override it.

// Create a closure so that we can define intermediary
// method pointers that don't collide with other items
// in the global name space. 

function monkeyPatchAutocomplete() {

    // don't really need this, but in case I did, I could store it and chain 
    var oldFn = $.ui.autocomplete.prototype._renderItem;
    var requestIndex = 0;
    $.ui.autocomplete.prototype._initSource = function() {
        // whatever
        console.log("Override method");
        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",
                    autocompleteRequest: ++requestIndex,
                    success: function(data, status) {
                        console.log("Override success function, handling request");
                        if (this.autocompleteRequest === requestIndex) {
                            response(data); //you handle both types of data here
                        }
                    },
                    error: function() {
                        console.log("Override error function, handling request");
                        if (this.autocompleteRequest === requestIndex) {
                            response([]);
                        }
                    }
                });
            };
        } else {
            this.source = this.options.source;
        }
    };
}


// When DOM is ready, initialize.
$(document).ready(function() {
    monkeyPatchAutocomplete();

    $("#birds").autocomplete({
        source: "http://jqueryui.com/demos/autocomplete/search.php",
        minLength: 2
    });

});

Then your code doesn't need to execute anything different, it just handles the differences and passes it along to the success method.

Here is a jsFiddle for this: http://jsfiddle.net/lemkepf/DAQ6s/5/ Note: the actual autocomplete wont work as the cross domain security is in place. You can open up firebug and see the console.debug lines fire when you start typing in the box.

Community
  • 1
  • 1
Paul Lemke
  • 5,494
  • 3
  • 47
  • 66
  • +1 thanks for the suggestions. "Monkeypatching" is new to me, and something I'll look at more closely when I have time. But what I'm really looking for is something which can be used by an internal team developing LOB apps; so I don't want to be taking the risk of overriding jQuery internals. I think I'll just stick with my own version for now. – Joe Jan 25 '12 at 12:17