Use different value from JSON data instead of displayKey using Typeahead

51,489

Solution 1

displayKey is used to indicate which key should be shown in the dropdown list and in the input field after the user has selected an entry.

If you want the entries shown in the dropdown list to be different from what ends up in the input field you need to use a custom template.

From the documentation:

suggestion – Used to render a single suggestion. If set, this has to be a precompiled template. The associated suggestion object will serve as the context. Defaults to the value of displayKey wrapped in a p tag i.e. <p>{{value}}</p>.

Something like this should work:

$('.typeahead').typeahead(null, {
    name: 'stocks',
    displayKey: 'company_name',
    source: stocks.ttAdapter(),
    templates: {
        suggestion: function (stock) {
            return '<p>' + stock.code + '</p>';
        }
    }
});

The submitted template will be used to show stock.code in the dropdown list, while stock.company_name will be shown in the input field after the user selects an entry.

Solution 2

I am no jQuery / JavaScript-Guru. So I prefer it the easy way. Just to understand what I did when I look at my code later...

Thanks to Eric Saupe I found a smart solution:

//JSON FILE
[
    {
        "company_name": "Facebook",
        "code": "fb",
    },
    {
        "company_name": "Google",
        "code": "goog",
    },
    {
        "company_name": "Yahoo",
        "code": "yhoo",
    },
    {
        "company_name": "Apple",
        "code": "aapl",
    },
    {
        "company_name": "Royal Mail",
        "code": "rmg.l",
    },
 ]

// JS SCRIPT

   var stocks = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('company_name'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote: 'javascripts/stockCodes.json'
    });

    stocks.initialize();

    $('.typeahead').typeahead(
        null, {
        name: 'stocks',
        displayKey: 'company_name',
        source: stocks.ttAdapter()
    }).on('typeahead:selected', function(event, data){            
        $('.typeahead').val(data.code);        
    });

Just use the typeahead custom events as described in the docs on github.

Hope, this helps (and when it's just for my own later reference).

JSFIDDLE

Solution 3

You need to use the ´displayKey´ variable. See below copy-paste from the typeahead docs:

displayKey – For a given suggestion object, determines the string representation of it. This will be used when setting the value of the input control after a suggestion is selected. Can be either a key string or a function that transforms a suggestion object into a string. Defaults to value.

https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md

Your specific code would then be something like this:

var stocks = new Bloodhound({
        datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.code); },
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        limit: 3,
        prefetch: {
            url: 'javascripts/stockCodes.json',
            filter: function(list) {
                // This should not be required, but I have left it incase you still need some sort of filtering on your server response
                return $.map(list, function(stock) { return { code: stock.code, company_name: stock.company_name }; });
            }
        }
    });

    stocks.initialize();

    $('.typeahead').typeahead(null, {
        name: 'stocks',
        displayKey: function(stock) {
           return stock.company_name;
        },
        source: stocks.ttAdapter()
    });

I had a very similar requirement on my own site, and I had this working with no issue. I have not tried this particular example though, so let me know if it works.

Remember to call stocks.clearPrefetchCache(); to clear your cache to easier track bugs.

Solution 4

If I read correctly, I believe this is what you want:

var stocksData = [{
    "Facebook": "fb",
}, {
    "Google": "goog",
}, {
    "Yahoo": "yhoo",
}, {
    "Apple": "aapl",
}, {
    "Royal Mail": "rmg.l",
}, ];

var stocks = new Bloodhound({
    datumTokenizer: function (d) {
                  for (var prop in d) {
                      return Bloodhound.tokenizers.whitespace(d[prop]);
                  }

    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    limit: 3,
    local: stocksData,
});

stocks.initialize();

$('input').typeahead(null, {
    name: 'stocks',
    displayKey: function(stocks) {
        for (var prop in stocks) {
            return prop;    
        }
    },
    source: stocks.ttAdapter()
});

You will of course want to change the local to prefetch/remote like you had for the json file.

UPDATE

FIDDLE

Share:
51,489
user1779796
Author by

user1779796

Updated on July 09, 2022

Comments

  • user1779796
    user1779796 almost 2 years

    I have started using Typeahead.js and am struggling to figure out a way of allowing a user to type and search for a company name, once selected input the associated company code.

    .json file:

    [{
        "company_name": "Facebook",
        "code": "fb",
    }, {
        "company_name": "Google",
        "code": "goog",
    }, {
        "company_name": "Yahoo",
        "code": "yhoo",
    }, {
        "company_name": "Apple",
        "code": "aapl",
    }, {
        "company_name": "Royal Mail",
        "code": "rmg.l",
    }]
    

    .js Script:

    var stocks = new Bloodhound({
        datumTokenizer: function(d) {
            return Bloodhound.tokenizers.whitespace(d.code);
        },
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        limit: 3,
        prefetch: {
            url: 'javascripts/stockCodes.json',
            filter: function(list) {
                return $.map(list, function(stock) {
                    return {
                        code: stock
                    };
                });
            }
        }
    });
    
    stocks.initialize();
    
    $('.typeahead').typeahead(null, {
        name: 'stocks',
        displayKey: 'code',
        source: stocks.ttAdapter()
    });
    

    Currently, this just displays the list of codes when the user types in the input field. However, I would like to know if there is a way to allow them to search on code but once selected, the value in the textbox to be company_name? Is this even possible using this plugin. Any help will be greatly appreciated.

    Thanks!

  • user1779796
    user1779796 about 10 years
    thank you for your answer, much appreciated. I have only just gotten around to using the code provided in the fiddle above. I'm not sure if I explained the problem properly but I am trying to figure out how to place the associated value in the input text box as opposed the the key value. For example, typing 'Facebook' should show Facebook in the drop down list however, once selected, the text box value would show 'fb' instead of 'Facebook'. I have tried tweaking the code provided, but haven't managed to the incorporate this functionality yet. Thank you for your help on this.
  • Alok
    Alok about 9 years
    You want to also handle 'typeahead:autocompleted'.
  • NLemay
    NLemay almost 8 years
    Seems like the best solution, but the JSFIDDLE doesn't work anymore. When clicking, it is writing the whole company name.
  • Old Geezer
    Old Geezer about 7 years
    Is it typeahead:selected or typeahead:select? Official docs say the latter. Either way, I couldn't get the event to fire. I added alert(data.code); to your fiddle and it didn't fire too. What should be the right way?
  • Tobse
    Tobse about 7 years
    The JSFIDDLE works as expected for me. I tested with Firefox and Chrome. I cannot tell if it's :selected or :select. It worked for me with :selected Meanwhile I switched over to jQuery Typeahead (runningcoder.org/jquerytypeahead/documentation). It is well documented and feature-rich and the config is more straightforward (imho).
  • Rehmat
    Rehmat about 5 years
    Even after using setTimeout method like: setTimeout(function() { $('.typeahead').val(data.code); }, 1);, it was changing the value back when leaving the input box. So, I had to move to @Esseb's template based solution.