Typeahead v0.10.2 & Bloodhound - 处理嵌套的JSON对象

20

更新

基于@BenSmith(https://stackoverflow.com/users/203371/BenSmith)的正确答案,我找到了我的问题,并发现我没有正确地遍历JSON层次结构。这是可以正常工作的代码:

        // 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>')
          }
      });

感谢 @BenSmith 的帮助!


原始问题

我刚开始使用 Typeahead 和 Bloodhound。文档不是很有用。我有一组从正在使用的 API 中返回的 JSON 对象。我正在尝试弄清楚如何浏览我的 JSON 对象,以便 Bloodhound 能够理解它们。

这里的目标是用户将开始输入歌曲名称。然后自动完成将显示演唱该歌曲的歌曲名称列表和艺术家。

例如:Chimes At Midnight by Mastodon

我正在使用每个库的最新版本: https://twitter.github.io/typeahead.js/

示例 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"}]}}

使用此网站 http://json.parser.online.fr/ 轻松格式化 JSON。

我将收到的 JSON 格式始终相同,但包含不同的数据。在此示例中,“曲目”为空。其他结果将有数据。我还需要能够访问该数据。

我正在使用最新版本的 JQuery。这是我在 html 页面中包含的内容:

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

以下是HTML代码:

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

这里是脚本:

    // 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()
      });
当我尝试运行这段代码时,出现了以下错误: Uncaught TypeError: Cannot read property 'tokens' of undefined 非常感谢任何帮助或指引。我只需要知道如何让我正在使用的JSON数据与Typeahead/Bloodhound配合工作。
谢谢!

很高兴我能帮到你,Jared :) - Ben Smith
2个回答

21

您需要编写筛选函数,以便创建用作数据的JavaScript对象数组。以下代码应该可以工作(我还没有测试过):

您需要编写筛选函数,以便创建用作数据的JavaScript对象数组。以下代码应该可以工作(我还没有测试过):
filter: function (response) {
            return $.map(response.songs, function (song) {
                return {
                    title: song.title,
                    artistName: song.artist_name
                };
            });
        }

(filter函数的示例可以在这里找到)

并将您的datumtokenizer更改为:

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

也将您的 displaykey 更改为:

displayKey: 'title'

由于这是Typeahead用于搜索的关键字,因此请务必保持不变。

至于在建议列表中显示歌曲名称和艺术家,请使用模板(例如Handlebars)显示结果。请参见Typeahead示例页面中的“自定义模板”示例。然后,您的建议标记将类似于以下内容:

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

你好,非常感谢您提供这个非常完整的答案!它真的帮助我更好地理解了这个问题。我已经实现了代码,没有出现错误。不过我仍然有一个问题。当我尝试搜索歌曲名称时,我的“空”模板消息会出现。由于某种原因,它无法在我输入歌曲名称时找到它们。为了简化这个问题,我在这里创建了一个JSFiddle:http://jsfiddle.net/VBLTS/2/ - Jared
忘了提到这一点。我遇到了这个JS错误:Uncaught TypeError: Cannot read property 'length' of undefined。 - Jared
1
我找到问题所在了。我没有正确地遍历 JSON。我调用了 response.songs,但它实际上应该是 response.response.songs,根据我的 JSON 结构。所以我把函数重命名为 data,现在调用 data.response.songs 一切正常。 - Jared
@user993514,看到了你在我的手机上的评论,但直到现在才有机会在我的电脑上帮助你。很高兴听到你的应用程序正在运行,并且我很高兴能够提供帮助 :) - Ben Smith

11
我的答案并没有什么新内容,只是针对twitter typeahead的v0.11进行了更新。与往常一样,如果您已经点赞,请将其转移到原始答案。
友好提醒给所有仍在使用Twitter-Typeahead的人,应该远离这个存储库,因为它已经不再维护。相反,请重定向到corejavascript/typeahead.js,这是原始存储库的一个分支,同一作者(@JakeHarding)维护此存储库。
从v0.10到v0.11,有些事情发生了变化,因此答案也必须更改。以下是我针对这个更新版本的新代码。
HTML
<div id="prefetch">
  <input class="typeahead" type="text" placeholder="Countries">
</div>

JS

    var tags = new Bloodhound({
        datumTokenizer: function(datum) {
            return Bloodhound.tokenizers.whitespace(datum.name);
        },
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        prefetch: {
            url: 'http://www.yourwebsite.com/js/data.json',
            cache: false,
            transform: function(response) {
                return $.map(response, function (tag) {
                    return {
                        name: tag.tagName,
                        id: tag.tagId
                    }
                });
            }
        }
    });

    $('#prefetch .typeahead').typeahead({
        minLength: 3,
        highlight: true
    }, {
        name: 'tags-dataset',
        source: tags,
        display: 'name'
    });

改变的内容:

  1. prefetch选项下没有filter选项。取而代之的是使用transform
  2. typeahead()函数下的displayKey已更改为简单的display
  3. transform选项下的cache设置为false,以便每次加载数据源。这对于测试很好,但在生产服务器上,如果需要缓存数据,则必须禁用此选项。

对于那些尝试使用不同数据源并将cache设置为true的人的额外说明。您必须始终在控制台中运行window.localStorage.clear();方法以清除已存在的本地存储,以便可以检索新数据。如果未能这样做,将导致使用旧数据,并且您可能会认为为什么您的代码无法工作。

这里有一个可行的JSFIDDLE示例供您参考。请注意以下几点:

  1. 我不知道typeahead是否有CDN,所以我只是简单地把整个typeahead JavaScript代码粘贴在那里。页面底部可以看到我的函数。
  2. 数据源指向一个链接,该链接是托管在我的Dropbox账户中的文件。如果我删除此文件,则此示例将完全无法工作。因此,您需要提供自己的来源。以下是该文件的内容 - [{"tagId":9,"tagName":"Action"},{"tagId":10,"tagName":"Adventure"},{"tagId":6,"tagName":"Books"},{"tagId":11,"tagName":"Fantasy"},{"tagId":2,"tagName":"Games"},{"tagId":1,"tagName":"Movies"},{"tagId":3,"tagName":"Music"},{"tagId":8,"tagName":"News"},{"tagId":4,"tagName":"Office"},{"tagId":5,"tagName":"Security"},{"tagId":7,"tagName":"TV-Shows"}]

不错的更新,Rash。感谢你让我知道corejavascript/typeahead.js! - Ben Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接