Twitter typeahead no result when using “prefetch”, but working with “remote” JSON
Asked Answered
A

1

5

i´m struggling the whole day. My typeahead search expression works perfect with remote json data. But when i try to use the same json data as prefetched data, the suggestion is empty. After hitting the first sign i get the predefined message "unable to find anything t..." for an empty result.

Here is my script:

function initialTypeaheadTest(){
    var mo_selector = $('*[data-moT="mysearch"]');
    var engine = new Bloodhound({
        limit: 10
        ,prefetch: {url:'/api/action/getjsontest'}
        //,remote: {url:'/api/action/getjsontest'}
        ,datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name')
        ,queryTokenizer: Bloodhound.tokenizers.whitespace
    });
    engine.clearPrefetchCache(); //  only for test purposes
    engine.initialize();

    mo_selector.typeahead(
        {
            hint: true,
            highlight: true,
            minLength: 1
        }, {
        name: 'engine',
        displayKey: 'name',
        source: engine.ttAdapter(),
            templates: {
                empty: [
                    '<div class="empty-message">',
                    'unable to find anything that match the current query',
                    '</div>'
                ].join('\n'),
                suggestion: Handlebars.compile([
                        '<div id="{{value}}"><p style="max-height:100%;"><strong>{{title}}</strong></p>',
                        '<span style="font-size:12px;">{{year}}</span></div>'
                    ].join(''))
            }
    });
}

and thats my json:

[{
"year":"02 Feb 2013 at 00:40 by anonymous",
"title":"JavaScript HTML DOM Changing HTML Content",
"value":"1",
"tokens":["JavaScript", "HTML", "DOM", "Changing", "HTML", "Content"]
},{
"year":"02 Feb 2013 at 00:42 by anonymous",
"title":"WDR.de",
"value":"2",
"tokens":["WDR.de"]
},{
"year":"02 Feb 2013 at 00:46 by anonymous",
"title":"Nachrichten, aktuelle Schlagzeilen und Videos - n-tv.de",
"value":"3",
"tokens":["Nachrichten", "aktuelle", "Schlagzeilen", "und", "Videos", "n", "tv.de"]
},{
"year":"02 Feb 2013 at 00:55 by anonymous",
"title":"JavaScript RegExp Object",
"value":"5",
"tokens":["JavaScript", "RegExp", "Object"]
},{
"year":"15 Feb 2013 at 23:24 by anonymous",
"title":"DotnetNuke Module Car Listing Module",
"value":"8",
"tokens":["DotnetNuke", "Module", "Car", "Listing", "Module"]
},{
"year":"08 Feb 2014 at 01:08 by advertiser",
"title":"Empfehlung",
"value":"1000",
"tokens":["Empfehlung"]
}]

original path: mattopen.com/api/action/getjsontest

for me, all looks good. The json data is well formed. All strings (e.g. in tokens) are quoted in double quotes as well. Why is remote data working but prefetch not?

can someone point me in the right direction? thanks

Antipodal answered 23/10, 2014 at 17:41 Comment(0)
M
7

Everything in your setup is good with the exception of the tokenizer that is being set. Because you are returning a json object type in the data array you have to specify what the property/field is in the json object that you want to tokenize. In your code you specify name but your json type does not contain name. If you change it from name to title everything works as expected and the title field is then searched.

Change Line:

datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name')

To:

datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title')

This is assuming the field you want to use title as the index to search. If you would prefer to search another field/property then change it to the name of that field/property that is present in your object type listed in the array.

If you want to use the tokens that you provided in your object you can do that to by providing a function that does exactly that. Replace the line datumTokenizer with this:

,datumTokenizer: function (yourObj) {
    return yourObj.tokens; // returns the tokens array in your object
}

By the way the default Bloodhound.tokenizers.obj.whitespace means that it will try to split a string with white spaces. This is also why you do not see it above in the last tokens implementation as I assume that your tokens field will always have whole strings that you want as is.

From the Bloodhound Documentation

datumTokenizer – A function with the signature (datum) that transforms a datum into an array of string tokens. Required.

I also added sufficient and set it to 1, this tells the engine that if at least 1 item is returned (from prefetch in this case) then a trip to the server is not necessary.

sufficient – If the number of datums provided from the internal search index is less than sufficient, remote will be used to backfill search requests triggered by calling #search. Defaults to 5.

Also, for testing you can create a file on disk (lets call it prefetchData.json) in the root directory of the site, copy your json data text directly into this file that you have listed in your question, and then change the url to point to it directly.

prefetch: {url:'/prefetchData.json'}

Here is the complete working code with changes mentioned above. Now if you type in J you should get 2 results returned.

function initialTypeaheadTest(){
    var mo_selector = $('*[data-moT="mysearch"]');
    var engine = new Bloodhound({
        limit: 10
        ,sufficient:1
        ,prefetch: {url:'/prefetchData.json'}
        //,remote: {url:'/api/action/getjsontest'} // left it commented out to prove it works
        ,datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title')
        ,queryTokenizer: Bloodhound.tokenizers.whitespace
    });
    engine.clearPrefetchCache(); //  only for test purposes
    engine.initialize();

    mo_selector.typeahead(
        {
            hint: true,
            highlight: true,
            minLength: 1
        }, {
        name: 'engine',
        displayKey: 'name',
        source: engine.ttAdapter(),
            templates: {
                empty: [
                    '<div class="empty-message">',
                    'unable to find anything that match the current query',
                    '</div>'
                ].join('\n'),
                suggestion: Handlebars.compile([
                        '<div id="{{value}}"><p style="max-height:100%;"><strong>{{title}}</strong></p>',
                        '<span style="font-size:12px;">{{year}}</span></div>'
                    ].join(''))
            }
    });
}
Microhenry answered 2/4, 2016 at 19:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.