Typeahead v0.10.2 и Bloodhound — Работа с вложенными объектами JSON

ОБНОВЛЕНИЕ

Основываясь на правильном ответе @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. Документация не очень полезна. У меня есть набор объектов JSON, которые я получаю из API, с которым работаю. Я пытаюсь понять, как перемещаться по моим объектам JSON, чтобы Bloodhound мог их понять.

Цель здесь состоит в том, чтобы пользователь начал вводить название песни. Затем автозаполнение отобразит список названий песен и исполнителей, которыми они были исполнены.

Например: Колокольчики в полночь от 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: Невозможно прочитать маркеры свойства undefined

Любая помощь или направление здесь будет принята с благодарностью. Мне просто нужно знать, как получить данные JSON, с которыми я работаю, работая с Typeahead/Bloodhound.

Спасибо!


person Jared    schedule 03.07.2014    source источник
comment
Рад, что смог помочь Джареду :)   -  person Ben Smith    schedule 04.01.2016


Ответы (2)


Вам нужно написать функцию фильтра, чтобы она создавала массив объектов javascript для использования в качестве данных. Следующее должно работать (я не проверял это):

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

(пример функции фильтра можно найти здесь)

И измените свой datumtokenizer на:

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

также измените свой дисплейный ключ на:

displayKey: 'title'

Так как это ключ, который Typeahead будет использовать для поиска.

Что касается отображения названия песни и исполнителя в списке предложений, я предлагаю вам использовать шаблоны (например, Handlebars) для отображения результатов. См. пример «Пользовательский шаблон» на странице примеров Typeahead. Разметка вашего предложения будет выглядеть следующим образом:

suggestion: Handlebars.compile('<p><strong>{{title}}</strong> by {{artistName}}</p>')
person Ben Smith    schedule 04.07.2014
comment
Здравствуйте, большое спасибо за этот очень хорошо собранный ответ! Это действительно помогло мне лучше понять это. Я реализовал код и больше никаких ошибок. У меня все еще есть проблема. Когда я пытаюсь найти название песни, появляется сообщение о пустом шаблоне. По какой-то причине он не может найти названия песен, когда я их печатаю. Для простоты я создал JSFiddle здесь: jsfiddle.net/VBLTS/2. - person Jared; 06.07.2014
comment
Забыл упомянуть об этом. Я получаю эту ошибку JS: Uncaught TypeError: Не удается прочитать свойство «длина» неопределенного - person Jared; 06.07.2014
comment
Я нашел проблему. Я неправильно перемещался по JSON. Я вызывал response.songs, когда действительно нужно было использовать response.response.songs на основе моей структуры JSON. Поэтому я переименовал функцию в данные и теперь вызываю data.response.songs, и все работает. - person Jared; 07.07.2014
comment
@ user993514 Увидел ваш комментарий на своем телефоне, но только сейчас смог помочь вам на своем ПК. Отличные новости, ваше приложение работает, и я рад, что смог помочь :) - person Ben Smith; 07.07.2014

В моем ответе нет ничего нового, а просто обновление версии 0.11 для твиттера. Как всегда, перенаправьте свои голоса (если есть) на исходный ответ.

Дружеское напоминание всем, кто все еще использует Twitter-Typeahead, — отказаться от этого репозитория, так как он больше не поддерживается. Вместо этого перенаправьте себя на corejavascript/typeahead.js, который является ответвлением исходного репозитория и тем же автором (@JakeHarding) поддерживает этот репозиторий.

С версии 0.10 до версии 0.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. Функция displayKey под typeahead() была изменена на просто display.
  3. cache в параметре transform установлено значение false, чтобы источник данных загружался каждый раз. Это хорошо для тестирования, но на рабочем сервере эту опцию нужно отключить, если требуется кэширование данных.

В качестве дополнительного примечания для тех, кто пробует этот пример с разными источниками данных, где для параметра cache установлено значение true. Вы всегда должны запускать метод window.localStorage.clear(); в своей консоли, чтобы очистить уже существующее локальное хранилище, чтобы можно было получить новые данные. В противном случае будут использоваться старые данные, и вы можете подумать, почему ваш код не работает.

Вот рабочий пример JSFIDDLE для начала. Несколько замечаний:

  1. Я не знал, есть ли CDN для typeahead, поэтому я просто вставил туда весь код javascript typeahead. Внизу страницы вы можете посмотреть мою функцию.
  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"}]
person Rash    schedule 02.01.2016
comment
Приятное обновление, Раш, и спасибо, что сообщили мне о corejavascript/typeahead.js! - person Ben Smith; 28.06.2016