Search code examples
jqueryjsonnestedtypeahead.jsbloodhound

Typeahead v0.10.2 & Bloodhound - Working With Nested JSON Objects


UPDATE

Based on the correct answer from @BenSmith (https://stackoverflow.com/users/203371/BenSmith) I was able to find my problem and found out I was not navigating through my JSON hierarchy properly. Here is the working code:

        // instantiate the bloodhound suggestion engine
    var engine = new Bloodhound({
        datumTokenizer: function (datum) {
            return Bloodhound.tokenizers.whitespace(datum.title);
        },
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        prefetch: {
            url: "SampleData.json",
            filter: function (data) {
                //console.log("data", data.response.songs)
                return $.map(data.response.songs, function (song) {
                    return {
                        title: song.title,
                        artistName: song.artist_name
                    };
                });
            }
        }
    });

    // initialize the bloodhound suggestion engine
    engine.initialize();

    // instantiate the typeahead UI
    $('#prefetch .typeahead').typeahead(
      {
          hint: true,
          highlight: true,
          minLength: 1
      },
      {
          name: 'engine',
          displayKey: 'title',
          source: engine.ttAdapter(),
          templates: {
              empty: [
              '<div class="empty-message">',
              'unable to find any results that match the current query',
              '</div>'
              ].join('\n'),
              suggestion: Handlebars.compile('<p><strong>{{title}}</strong> by {{artistName}}</p>')
          }
      });

Thanks @BenSmith for the help!


Original Question

I am new to working with Typeahead and Bloodhound. The documentation is not very helpful. I have a set of JSON objects that I get back from an API I am working with. I am trying to figure out how to navigate through my JSON objects so that Bloodhound can understand them.

The goal here is a user will start to type a song name. The autocomplete will then display a list of song names and the artist it was performed by.

For Example: Chimes At Midnight by Mastodon

I am using the latest versions of each library: https://twitter.github.io/typeahead.js/

The sample JSON (SampleData.json):

{"response": {"status": {"version": "4.2", "code": 0, "message": "Success"}, "songs": [{"title": "Chimes At Midnight", "artist_name": "Mastodon", "artist_foreign_ids": [{"catalog": "7digital-AU", "foreign_id": "7digital-AU:artist:29785"}, {"catalog": "7digital-UK", "foreign_id": "7digital-UK:artist:29785"}], "tracks": [], "artist_id": "ARMQHX71187B9890D3", "id": "SOKSFNN1463B7E4B1E"}, {"title": "Down Under", "artist_name": "Men at Work", "artist_foreign_ids": [{"catalog": "7digital-AU", "foreign_id": "7digital-AU:artist:50611"}], "tracks": [], "artist_id": "AR4MVC71187B9AEAB3", "id": "SORNNEB133A920BF86"}]}}

Use this site http://json.parser.online.fr/ to format the JSON easily.

The JSON I will get back will always be in the same format, but contain different data. In this example, the "tracks" are null. Other results will have data. I would also need to be able to access that data as well.

I am using the latest version of JQuery. Here is what I have included in my html page:

<script src="Scripts/jquery-2.1.1.min.js"></script>
<script src="Scripts/typeahead.bundle.min.js"></script>

Here is the HTML:

<div id="prefetch">
    <input class="typeahead" type="text" placeholder="Songs...">
</div>

Here is the script:

    // instantiate the bloodhound suggestion engine
    var engine = new Bloodhound({
        datumTokenizer: function (d) { return Bloodhound.tokenizers.whitespace(d.tokens.join(' ')); },
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        prefetch: {
            url: "SampleData.json",
            filter: function (response) {
                return response.engine;
            }
        }
    });

    // initialize the bloodhound suggestion engine
    engine.initialize();

    // instantiate the typeahead UI
    $('#prefetch .typeahead').typeahead(
      {
          hint: true,
          highlight: true,
          minLength: 1
      },
      {
          name: 'songs',
          displayKey: function (engine) {
              return engine.songs.artist_name;
          },
          source: engine.ttAdapter()
      });

When I try to run this code I get the following error:

Uncaught TypeError: Cannot read property 'tokens' of undefined

Any help or direction here would be greatly appreciated. I just need to know how to get the JSON data I am working with working with Typeahead/Bloodhound.

Thanks!


Solution

  • You need to write the filter function so that it creates an array of javascript objects to use as the datums. The following should work (I haven't tested this):

    filter: function (response) {
                return $.map(response.songs, function (song) {
                    return {
                        title: song.title,
                        artistName: song.artist_name
                    };
                });
            }
    

    (an example of the filter function can be found here)

    And change your datumtokenizer to:

    datumTokenizer: function (datum) {
            return Bloodhound.tokenizers.whitespace(datum.title);
        }
    

    also change your displaykey to:

    displayKey: 'title'
    

    As this is the key which Typeahead will be using for searching.

    As for displaying the song name and artist in the list of suggestons, I suggest you use templating (e.g. Handlebars) for displaying the results. See the "Custom Template" example in Typeahead's examples page. Your suggestion mark-up will then look similar to the following:

    suggestion: Handlebars.compile('<p><strong>{{title}}</strong> by {{artistName}}</p>')