Bootstrap Typeahead with AJAX source: not returning array as expected

ajax, bootstrap-typeahead, javascript, jquery, twitter-bootstrap

Solution

Update 2

Ah, your service returns items that actually don't match the query `parm`. Your typeahead query is `'parm'`, with which none of the returned results match. You can override the `matcher` function used by the typeahead plugin, see the docs. Simply implement it as `return true` to match all results returned by your service.

Update 1

This is an updated version, that maps the name to an id, which can be used later. A jsfiddle is available.

var nameIdMap = {};

$('#lookup').typeahead({
    source: function (query, process) {
        return $.ajax({
            dataType: "json",
            url: lookupUrl,
            data: getAjaxRequestData(),
            type: 'POST',
            success: function (json) {
                process(getOptionsFromJson(json));
            }
        });
    },
    minLength: 1,
    updater: function (item) {
        console.log('selected id'+nameIdMap[item]);
        return item;
    }
});

function getOptionsFromJson(json) {
    $.each(json, function (i, v) {
        nameIdMap[v.name] = v.id;
    });

    return $.map(json, function (n, i) {
        return n.name;
    });
}

Original answer

You need to make the call async and call the `process` callback from within the success callback like this:

$('#companyNameInput').typeahead({
    source: function (query, process) {
        $.ajax({
            url: ROOT + 'Record/checkCompanyName',
            // async: false, // better go async
            data: 'q=' + query,
            type: 'POST',
            cache: false,
            success: function (data) {
                var companiesFinal = ... // snip
                process(companiesFinal);
            }
        })
    }
});

The `return ['test1', 'test2'];` works, because the source function is then basically set to:

// do ajax stuff, but do nothing with the result
// return the typeahead array, which the typeahead will accept as the result:
return ['test1', 'test2'];

Notes

There is a one-liner to fill `companiesData`:

var companiesFinal = return $.map(data, function (n, i) { n.name; });

And you probably want to declare your variables using `var`; otherwise they'll have global scope, which will bite you.

Problem

I am having a lot of trouble getting bootstraps typeahead to work properly with an AJAX source. If I alert out the array that is being returned it is perfectly fine, even when I hard code it to test it. It seems to return nothing most of the time or only a few items from the array when you have typed in a near match. Here is what I have at the moment: ``` $('#companyNameInput').typeahead({ source: function(query, process){ $.ajax({ url: ROOT+'Record/checkCompanyName', async: false, data: 'q='+query, type: 'POST', cache: false, success: function(data) { companiesFinal = []; map = {}; companies = $.parseJSON(data); $.each(companies, function(i, v){ map[v.name] = v.id; companiesFinal.push(v.name); }) } }) process(companiesFinal); // return ['test1', 'test2'] This works fine return companiesFinal; } ``` Does anyone have an idea why this is working properly? Here is an example of the object array returned from my PHP script. Objects with IDs 1 and 1216 show up on the typeahead dropdown, but non of the others do. I can not see any patterns or clue as to why only these would show and not the others. ``` [    {       "id": "1265",       "score": "40",       "name": "LMV AB"    },    {       "id": "10834",       "score": "33",       "name": "Letona"    },    {       "id": "19401",       "score": "33",       "name": "Lewmar"    },    {       "id": "7158",       "score": "33",       "name": "Lazersan"    },    {       "id": "3364",       "score": "33",       "name": "Linpac"    },    {       "id": "1216",       "score": "33",       "name": "L H Evans Limted"    },    {       "id": "1",       "score": "33",       "name": "LH Evans Ltd"    },    {       "id": "7157",       "score": "33",       "name": "Lazersan"    } ] ``` And finally the array that is past in `process(companiesFinal)`: ``` ["LMV AB", "Letona", "Lewmar", "Lazersan", "Linpac", "L H Evans Limted", "LH Evans Ltd", "Lazersan"] ``` Anyone have any clue? I am still totally clueless as to why this isn't working still :( ``` $('#companyNameInput').typeahead({ source: function(query, process){ companyTOut = setTimeout(function(){ return $.ajax({ url: ROOT+'Record/checkCompanyName', data: 'q='+query, type: 'POST', dataType: 'json', cache: false, success: function(data) { var count = 0; var companiesFinal = []; map = []; $.each(data, function(i, v){ map[v.name] = [v.id, v.score]; companiesFinal.push(v.name); }) process(companiesFinal); } }) }, 250) }, minLength: 2, highlighter: function(item) { $('#companyNameInput').closest('.control-group').removeClass('success') companyLocked = false; return '<span class="unselectable" title="'+map[item].score+'">'+item+'</span>'; }, updater: function(item) { selectedEntityId = map[item][0]; selectedCountryScore = map[item][1]; lockCompany(selectedEntityId); return item; } }); ``` ``` $output .= '['.$output; foreach($results as $result) { $output .= '{"id":"'.$result['id'].'",'; $output .= '"score":"'.$result['score'].'",'; $output .= '"name":'.json_encode($result['name']).'},'; } header('Content-Type: application/json'); echo substr($output, 0, strlen($output)-1).']'; ``` Console output for "parm": ``` [Object, Object, Object, Object, Object, Object] 0: Object id: "25024" name: "part" score: "75" __proto__: Object 1: Object id: "15693" name: "pari" score: "75" __proto__: Object 2: Object id: "28079" name: "Pato" score: "50" __proto__: Object 3: Object id: "18001" name: "PASS" score: "50" __proto__: Object 4: Object id: "15095" name: "PSR" score: "33" __proto__: Object 5: Object id: "22662" name: "PRP" score: "33" __proto__: Object length: 6 __proto__: Array[0] ```

Original source