Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can multiple inputs each using typeahead.js with different sources be consolidated?

I have an form with numerous text inputs for which 10 of them I'd like to use typeahead.js with Bloodhound. I have it working for 2 of them - they both include prefetch and remote data sources. But there's a fair amount of jQuery code for each instance of typeahead/Bloodhound and I'm curious if anyone has tried to "genericize" typeahead/Bloodhound to handle multiple different input elements each with a different data source? It might be more trouble than it's worth, but I'm a bit concerned about the amount of code the page will have to load.

My environment is Spring/MVC, Hibernate (Oracle db), Bootstrap.

Here's a sample of one typeahead/Bloodhound instance. It's in a function because I'm adding row's of inputs dynamically so I have to call this function after a new row is added to enable typeahead on the text input in that row.

function initIngredientsTA() {
    //set the options
    var ingredBH = new Bloodhound({
        limit: 20,
        datumTokenizer: function(datum) {
            return Bloodhound.tokenizers.whitespace(datum.name);
        },
        queryTokenizer: Bloodhound.tokenizers.whitespace,       
        prefetch: {
            url: '/myapp/resources/ingredients.json',
            cache: false,
            filter: function(data) {
                console.log("data: " + data.ingredients);
                return $.map(data.ingredients, function (ingredient) {
                    return {
                        id : ingredient.id,
                        name : ingredient.name
                    };
                });
            } 
        },
        remote: {
            url: '/myapp/recipe/addRecipe/getIngredients?searchStr=%QUERY',
            cache: false,
            wildcard: '%QUERY',
            filter: function(data) {
                console.log("data: " + data);
                return $.map(data, function (data) {
                    return {
                        id : data.id,
                        name : data.name
                    };
                });
            } 
        }
    });

    //initialize the suggestion Engine
    ingredBH.initialize();

    $('.ingredDesc').typeahead(
    {
        hint: true,
        highlight: true,
        minLength: 1
    },
    {
        name: 'ingredients',
        displayKey: 'name',
        limit: 20,
        source: ingredBH.ttAdapter(),
    })
};

EDIT: I think what I'm really asking is if anyone has created a "template" version of typeahead/Bootstrap that can then be instantiated for each individual text input.

like image 337
LWK69 Avatar asked Jun 22 '15 13:06

LWK69


People also ask

What is a Typeahead input?

The typeahead input fields are very popular in modern web forms. The main purpose of using typeahead is to improve the user experience by supplying hints or a list of possible choices based on the text they've entered while filling a form or searching something — like the Google instant search.

What is Typeahead API?

Performs search to retrieve list of places by input text and location vicinity. Address Autocomplete takes free form text as input. It could be a part of an address. Location could be provided either as latitude/longitude or as country ISO code. It returns consolidated list of addresses based on the input text.

What is Typeahead jQuery?

by Tom Bertrand. jQuery plugin that provides Typeahead (autocomplete) Search preview from Json object(s) via same domain Ajax request or cross domain Jsonp and offers data compression inside Local Storage. The plugin is built with a lot of options and callbacks to allow customization.


1 Answers

Sorry if this was a question with an obvious answer, but I'm new to the Java, Spring/Hibernate, jQuery, etc., stack. Using Firebug I was able to figure out what both typeahead and Bloodhound required and came up with the following:

//token and filter functions
function ingredDatumToken(datum) {
    return Bloodhound.tokenizers.whitespace(datum.name);
}

function ingredPrefetchFilter(data) {
    return $.map(data.ingredients, function (ingredient) {
        return {
            id : ingredient.id,
            name : ingredient.name
        };
    });
};

function ingredRemoteFilter(data) {
    return $.map(data, function (data) {
        return {
            id : data.id,
            name : data.name
        };
    });
};

//Bloodhound initialization
function initBloodhound(limit, cache, datumToken, queryToken,prefetchUrl,prefetchFilter, remoteUrl, wildcard, remoteFilter)
{
    var token = Bloodhound.tokenizers.whitespace; 

    var options = {};
    var prefetchOptions = {};
    var remoteOptions = {};

    prefetchOptions['url'] = prefetchUrl;
    prefetchOptions['cache'] = cache;
    prefetchOptions['filter'] = prefetchFilter;

    remoteOptions['url'] = remoteUrl;
    remoteOptions['cache'] = cache;
    remoteOptions['wildcard'] = wildcard; 
    remoteOptions['filter'] = remoteFilter;       

    options['limit'] = limit; 
    options['datumTokenizer'] = datumToken === null ? token : datumToken;
    options['queryTokenizer'] = queryToken === null ? token : queryToken;

    if (prefetchUrl != null)
        options['prefetch'] = prefetchOptions;

    if (remoteUrl != null)
        options['remote'] = remoteOptions;

    return new Bloodhound(options);
};

//create two Bloodhound engines
var ingredBH = initBloodhound(50,false,ingredDatumToken,null,'/myapp/resources/ingredients.json',ingredPrefetchFilter,'/myapp/recipeaddRecipe/getIngredients?searchStr=%QUERY','%QUERY',ingredRemoteFilter);
var measureBH = initBloodhound(20,false,null,null,'/myapp/resources/measures.json',null,null,null,null);
//add more Bloodhound engines here

//typeahead options
function initTypeaheadOptions(hint, highlight, minLength) {

    var options = {};

    options['hint'] = hint;
    options['highlight'] = highlight;
    options['minLength'] = minLength;

    return options;
}

//typeahead dataset
function initTypeaheadDataset(name, displayKey, limit, source) {

    var datasets = {};

    datasets['name'] = name;
    datasets['displayKey'] = displayKey;
    datasets['limit'] = limit;
    datasets['source'] = source;

    return datasets;
}

//initialize a typeahead control    
function initIngredientsTA() {

    var options = initTypeaheadOptions(true,true,1);
    var dataset = initTypeaheadDataset('ingredients', 'name', 20, ingredBH);

    $('.ingredDesc').typeahead(options,dataset);
};

//initialize a typeahead control
function initMeasuresTA() {

    var options = initTypeaheadOptions(true,true,1);
    var dataset = initTypeaheadDataset('measures', null, 20, measureBH);

    $('.ingredQtyType').typeahead(options,datasets);
};

//add more typeahead initialization functions here

//call the initialize functions
initIngredientsTA();
initMeasuresTA();
//call more initialize functions here

I'm still working on making it more generic and I'm not crazy about all the parameters in the call to the Bloodhound initialization function, but since I'll have about 10 or more typeahead-enabled controls on the page it will be fairly easy to add the rest of them with just a few lines of code. Those typeahead controls that are not part of the dynamically created rows on the page won't need separate functions for initialization, but can be initialized with just 3 lines of code. I'm definitely open to any comments or suggestions for improvement, including any thoughts that this is a stupid idea.

like image 142
LWK69 Avatar answered Nov 26 '22 22:11

LWK69