Typeahead.js: Cannot create POST request with Bloodhound

Created on 13 May 2015  路  8Comments  路  Source: twitter/typeahead.js

As far as I can tell, src/bloodhound/remote.js has been refactored in a way that it is hardcoded to use GET requests such that the few examples of how to do a post request from the past that I can find no longer work (ajax object has been replaced by transport, and remote.js passes in hardcoded settings.

I have tried to use an transport in the override but this doesn't seem to work either (it still shows GET in the breakpoint for the actual ajax call).

    remote : {
            transport : function (url, options, onSuccess, onError) {
                options = { type: 'POST', dataType: 'json' };
                $.ajax(url, options).done(done).fail(fail).always(always);

                function done(data, textStatus, request) {
                    onSuccess(data);
                }

                function fail(request, textStatus, errorThrown) {
                    onError(errorThrown);
                }

                function always() {

                }
            }
    }

Most helpful comment

Use prepare.

                remote: {
                   url: '/my/post',
                   prepare: function (query, settings) {
                      settings.type = "POST";
                      settings.contentType = "application/json; charset=UTF-8";
                      settings.data = JSON.stringify(query);

                      return settings;
                   }

settings is the jQuery AJAX options object.

All 8 comments

Managed to resolve it using

                var post = {
                    url: "localhost:8080/",
                    data: {query: $('#remote .typeahead').val()},
                    type: 'post',
                    dataType: 'json',
                    headers: {'content-type': 'application/json'}
                };
                $.ajax(post, undefined).done(done).fail(fail).always(always);

Why they forbid people to use POST? Waste so much to figure out the problem is typeahead.

Use prepare.

                remote: {
                   url: '/my/post',
                   prepare: function (query, settings) {
                      settings.type = "POST";
                      settings.contentType = "application/json; charset=UTF-8";
                      settings.data = JSON.stringify(query);

                      return settings;
                   }

settings is the jQuery AJAX options object.

I don't know if the above by kamranayub works, but the solution I posted prior only works for the first call, so I gave up and switched to using jquery.autocomplete. It also doesn't support POST out of the box, requiring an override. But I found the script much easier to read and understand while debugging why things failed. I feel it has better documentation for features as well. Typeahead has barely any documentation for use cases that are different than the samples and the script is ridiculously convoluted to read. The other script is also smaller, I recommend that over typeahead.

@kamranayub solution works on the latest version (0.11.1). Thanks!

I am trying to use this technique to specify the AJAX timeout, but adding a:

settings.timeout = 60000;

for example, has no effect.

Can anyone suggest how to specify the ajax timeout in the prepare function?

@kamranayub - I am not able to set custom http request headers in below solution like this.

remote: {
               url: '/my/post',
               prepare: function (query, settings) {
                  settings.type = "POST";
                  settings.contentType = "application/json; charset=UTF-8";
                  settings.eid = 'xyz';
                  settings.data = JSON.stringify(query);

                  return settings;
               }

Any suggestions?

@kamranayub , Thanks it works.
I have mentioned complete working example of Local+ Remote,
if results are not found in localdataset then it will look into remote.

var lvSearchString = new Bloodhound({
    local: $.map(LocalData, function (pCustomerData) {
            let lvCustomerInfo = '';
            if(pSearchFieldType == 'name'){
                lvCustomerInfo = (pCustomerData["customer_name"]?pCustomerData["customer_name"]:"");
            }else if(pSearchFieldType == 'email'){
                lvCustomerInfo = (pCustomerData["email_id"]?pCustomerData["email_id"]:"");
            }else if(pSearchFieldType == 'phone'){
                lvCustomerInfo = (pCustomerData["phone_no"]?pCustomerData["phone_no"]:"");
            }
            return {
                completeUserInfo:lvCustomerInfo,
                name: pCustomerData["customer_name"],
                customerId: pCustomerData["customer_source_id"],
                email: pCustomerData["email_id"],
                userId: pCustomerData["user_id"],
                bizCustomerId: pCustomerData["biz_customer_id"],
                shipping_address: pCustomerData["shipping_address"],
                phone: pCustomerData["phone_no"]
            };
        }),
        datumTokenizer: function (datum) {
            let lvReturnValue = '';
            if(pSearchFieldType == 'name'){
                lvReturnValue = Bloodhound.tokenizers.whitespace(datum.name);
            }else if(pSearchFieldType == 'email'){
                lvReturnValue = Bloodhound.tokenizers.whitespace(datum.email);
            }else if(pSearchFieldType == 'phone'){
                lvReturnValue = Bloodhound.tokenizers.whitespace(datum.phone);
            }else{
                let lvNameValue = Bloodhound.tokenizers.whitespace(datum.name);
                let lvEmailValue = Bloodhound.tokenizers.whitespace(datum.email);
                let lvPhoneValue = Bloodhound.tokenizers.whitespace(datum.phone);
                lvReturnValue = lvNameValue.cancat(lvEmailValue).concat(lvPhoneValue);
            }
            return lvReturnValue;
        },
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote: {
            url: '/customers/search.json?access_token=<your_api_key_if_any>',
            rateLimitBy: 'debounce',
            rateLimitWait: 400,
            filter: function(response){
                return $.map(response.data, function (pCustomerData) {
                    console.log(pCustomerData);
                    let lvCustomerInfo = '';
                    if(pSearchFieldType == 'name'){
                        lvCustomerInfo = (pCustomerData["customer_name"]?pCustomerData["customer_name"]:"");
                    }else if(pSearchFieldType == 'email'){
                        lvCustomerInfo = (pCustomerData["email_id"]?pCustomerData["email_id"]:"");
                    }else if(pSearchFieldType == 'phone'){
                        lvCustomerInfo = (pCustomerData["phone_no"]?pCustomerData["phone_no"]:"");
                    }
                    return {
                        name: pCustomerData["customer_name"],
                        email: pCustomerData["email_id"],
                        bizCustomerId: pCustomerData["biz_customer_id"],
                        phone: pCustomerData["phone_no"]
                    };
                })
            },
            prepare: function (query, settings) {
                settings.type = "POST";
                settings.contentType = "application/json; charset=UTF-8";
                settings.data = JSON.stringify({
                    search_query: query,
                    search_field: (pSearchFieldType=='name'?'customer_name':(pSearchFieldType=='phone'?'phone_no':(pSearchFieldType=='email'?'email_id':'')))
                });
                settings.xhrFields = {withCredentials: true};
                return settings;
            }
        }    
Was this page helpful?
0 / 5 - 0 ratings