سؤال

I seem to have the same problem as Twitter Typeahead Ajax results undefined but that has gone unsolved, so I am asking again, hopefully I can provide some missing details.

I am using the standalone typeahead 0.10.2 along with bootstrap 2.3.1. Underscore is 1.6.0. Complete list of included libraries is:

<script src="/static/portal/js/jquery-1.9.1.min.js"></script>
<script src="/static/portal/js/jquery-ui-1.10.2.custom.min.js"></script>
<script src="/static/portal/js/bootstrap.min.js"></script>
<script src="/static/portal/js/typeahead.bundle.min.js"></script>
<script src="/static/portal/js/hogan-2.0.0.min.js"></script>
<script src="/static/portal/js/underscore-min.js"></script>

For my first try, I modelled my script on http://fusiongrokker.com/post/heavily-customizing-a-bootstrap-typeahead, and everything seems to work, right up to the call to process(). Rather than populating the autosuggest drop-down with my results, it only contains 'undefined' values (the right number of elements though).

$("#create_track").click(function() {
    var names_id_map = {};
    $('#ul_results').addClass('hide');
    $('#create_track_form').removeClass('hide');
    // use debounce from underscore.js to throttle requests to server
    var suggestArtists = _.debounce(function(query, process) {
        var artist_names = [];
        $.get(ARTIST_NAMES_URL, { q: query }, function (data) {
            // data = {"results": [{"id": "artist_123", "name": "xyz"}, ...]}
            $.each(data, function (key, val) {
                if (key == "results") {
                    $.each(val, function (i, item) {
                        artist_display = item.name + ' - ' + item.id;
                        artist_names.push(artist_display);
                        names_id_map[artist_display] = item.id;
                    });
                }
            });
            process(artist_names);
        });
    }, 300); // rate limit requests to server

    $("#artist_name").typeahead(null, {
        name: 'artist-names',
        minLength: 1,
        source: function (query, process) {
            suggestArtists(query, process);
        },
        updater: function (item) {
            console.dir('updater: ' + item); // never output
            $("#artist_id").val(names_id_map[item]);
            // return value which should be selected in drop-down
            // return item;
        },
    });

    return false;
});

That particular tutorial may have been created for the old bootstrap-bundled version of typeahead. Using the documentation for the newer standalone version of typeahead, I came up with this, which behaves the same, resulting in a menu of the right number of undefineds:

$("#create_track").click(function() {
    var names_id_map = {};
    $('#ul_results').addClass('hide');
    $('#create_track_form').removeClass('hide');

    var artistNames = new Bloodhound({
        name: 'artist-names',
        datumTokenizer: Bloodhound.tokenizers.whitespace,
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote: {
            url: ARTIST_NAMES_URL + '?q=%QUERY',
            //rateLimitBy: debounce, // debounce is not defined?
            rateLimitWait: 300,
            filter: function(data) {
                var artist_names = [];
                // data = {"results": [{"id": "artist_123", "name": "xyz"}, ...]}
                $.each(data, function (key, val) {
                    if (key == "results") {
                        $.each(val, function (i, item) {
                            artist_display = item.name + ' - ' + item.id;
                            artist_names.push(artist_display);
                            names_id_map[artist_display] = item.id;
                        });
                    }
                });
                return artist_names;
            },
        }
    });
    artistNames
        .initialize()
        .done(function() { console.log('artistNames init success'); });

    $("#artist_name").typeahead(null, {
        minLength: 1,
        source: artistNames.ttAdapter(),
    });

    return false;
});

If I populate it with local data like this:

$("#create_track").click(function() {
    var names_id_map = {};
    $('#ul_results').addClass('hide');
    $('#create_track_form').removeClass('hide');

    var artistNames = new Bloodhound({
        name: 'artist-names',
        datumTokenizer: Bloodhound.tokenizers.whitespace,
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        local: ["Abc", "Def", "Ghi", "Jkl"],
    });
    artistNames.initialize()
        .done(function() { console.log('artistNames init success'); });

    $("#artist_name").typeahead(null, {
        minLength: 1,
        source: artistNames.ttAdapter(),
    });

    return false;
})

I can type "a" or "d", for example, and get a single "undefined" suggestion. Typing "b", "c", or "z" gives no suggestion.

I've read just about everything I could find about typeahead on Google & SO over the last day. I am confident my JSON is decoded properly, and in all cases my artist_names is a native JS array of strings.

هل كانت مفيدة؟

المحلول

Re-starting with the examples at https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md#api (and prompted by this other duplicate Why does bloodhound.get() return undefined?), I realized my array of local values in fact needs to be a hash/dictionary in co-operation with datumTokenizer & the typeahead's displayKey. I have the simple example now working for single-leading letter suggestions with:

var artistNames = new Bloodhound({ 
    name: 'artist-names',
    datumTokenizer: function (d) { return d.d; },
    queryTokenizer: Bloodhound.tokenizers.whitespace, 
    local: [{"d": "Abc"}, {"d": "Aaa"}, {"d": "Aab"}, {"d": "Def"}, {"d": "Ghi"}, {"d": "Jkl"}, {"d": "John Coltrane"}], 
}); 
artistNames.initialize().done(function() { alert('setup ok'); });

$("#artist_name").typeahead(null, { 
    minLength: 1, 
    displayKey: "d",
    source: artistNames.ttAdapter(), 
});

My initial syntax came from some of the tutorials I had found via google which were probably using the old bootstrap version of the library (ex. from the one I linked to @ top of this post: "Then a simple array of names (['John Smith','Jane Smith',...]) is passed to the process() callback.").

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top