Довольно часто при разработке проекта требуется создать поле поиска или ввода данных, в которое передаются данные автоподстановки. Например, поиск названия штата по нескольким символам. Этот пример со статическими данными можно найти на странице проекта Twitter Bootstrap – он прост и информативен:

typeahead

Код примера:

<input type="text" data-provide="typeahead" data-items="4" 
data-source='["Alabama","Alaska","Arizona","Arkansas","California","Colorado","Connecticut",
"Delaware","Florida","Georgia","Hawaii","Idaho","Illinois","Indiana","Iowa","Kansas",
"Kentucky","Louisiana","Maine","Maryland","Massachusetts","Michigan","Minnesota",
"Mississippi","Missouri","Montana","Nebraska","Nevada","New Hampshire","New Jersey",
"New Mexico","New York","North Dakota","North Carolina","Ohio","Oklahoma","Oregon",
"Pennsylvania","Rhode Island","South Carolina","South Dakota","Tennessee","Texas",
"Utah","Vermont","Virginia","Washington","West Virginia","Wisconsin","Wyoming"]'>

Для работы с Twitter Bootstrap нам понадобиться подключить сам скрипт Twitter Bootstrap и файл стилей используемый этим скриптом:

<head>
   <link href="assets/css/bootstrap.css" rel="stylesheet">
   <script src="assets/js/bootstrap.js"></script>
</head>

Но в большинстве случаев нам необходимы данные из БД, например поиск пользователя по имени. В этом случае нам необходим запрос к БД при помощи ajax:

<!-- поле ввода имени -->
<input type="text" name="name">
<input type="text" name="email">
<input type="text" name="phone">
<!-- подключаем typeahead  -->
<script>
$('input[name=username]').typeahead({
        //источник данных
        source: function (query, process) {
           return $.post('getusername', {'name':query}, 
                 function (response) {
                      var data = new Array();
                      //преобразовываем данные из json в массив
                      $.each(response, function(i, name)
                      {
                        data.push(i+'_'+name);
                      })
                      return process(data);
                    },
                 'json'
                 );
          }
          //источник данных
          //вывод данных в выпадающем списке
          , highlighter: function(item) {
              var parts = item.split('_');
              parts.shift();
              return parts.join('_');
          }
          //вывод данных в выпадающем списке
          //действие, выполняемое при выборе елемента из списка
          , updater: function(item) {
                     var parts = item.split('_');
                     var userId = parts.shift();
                     $.post('getuserdata', {'user_id':userId},
                          function (user) {
                            $('input[name=email]').val(user.email);
                            $('input[name=phone]').val(user.phone);
                          },
                       'json'
                      );
                      return parts.join('_');
                   }
          //действие, выполняемое при выборе елемента из списка
          }
);
</script>

В этом примере производится 2 запроса к БД: 1й выводит имена пользователей и получает id выбранного пользователя, а второй на основании id – запрашивает и выводит в соответствующие поля email и номер телефона выбранного пользователя. При помощи использования разделителя “_” data.push(i+’_’+name); мы в одном запросе получаем два параметра, это оправдано при небольшом количестве данных, таких, как id и имя, остальные данные мы получаем во втором запросе var parts = item.split(‘_’); var userId = parts.shift();.

Ниже 2 экшена на примере фреймворка Kohana с подключенным и настроенным модулем БД и ORM, для чистого PHP необходимо произвести подключение к БД и написание запроса с последующим выводом данных.

public function action_getname() {
            $users = ORM::factory('user')
                     ->where('name', 'LIKE', '%' . $_POST['name'] . '%')
                     ->find_all()
                     ->as_array('id', 'name');
            echo json_encode($users);
}

public function action_getuserdata() {
            $user = ORM::factory('user', $_POST['usr_id']);
                     ->as_array('id', 'name');
            echo json_encode($user);
}

Всевозможные детализации и проверки в данном примере не приводятся для упрощения кода.

10 thoughts on “Автокомплит с использованием ajax при помощи Twitter Bootstrap typeahead

  1. Интересное решение , спасибо !
    Жаль что нет “живого примера” как это работает на сервере …
    ну и вопрос =) как же без него
    А если нужно вместо значения записать id но выводить значение
    пример нужно показать Alabama но что бы в $_POST попал его id
    $_POST[‘district’] = 1

    Reply
    • Можно решить путем внесения требуемого значения в скрытое поле.
      Т.е. выпадающий список это для пользователя, а для обработки –

      <input type="hidden" name="hidden_id"/>

      часть кода из примера:

      , updater: function(item) {
                           var parts = item.split('_');
                           var userId = parts.shift();
                           $(input[name=hidden_id]).val(userId);
                            return parts.join('_');
                         }
                //действие, выполняемое при выборе елемента из списка
                }
      
      Reply
  2. Можно ли из updater передать массив с новыми данными в source? И как это сделать?
    Например, есть список товаров. У каждого товара есть производитель. Пользователь начинает набирать название производителя. Автокомплит ему помогает, но теперь надо еще дописать название товара.
    Можно, конечно, сразу в source передавать массив из “производитель товар”, но тогда выпадающий список подсказок может забиться только одним производителем.

    Reply
    • Я думаю все же правильно отдавать такие данные с сервера. Т.е. передавая на сервер символы вы в первую очередь ищете в таблице производителей и только когда найден производитель – начинаете выбирать данные из таблицы товаров.
      А по поводу забьет одним производителем – так это нормально, если нет других совпадений – то выводим только то, что попало под фильтр.
      Возможно есть смысл искать производителей до получения, например, пробела, а уже после этого искать товары с учетом этого производителя.

      Reply
  3. “Я думаю все же правильно отдавать такие данные с сервера. Т.е. передавая на сервер символы вы в первую очередь ищете в таблице производителей и только когда найден производитель – начинаете выбирать данные из таблицы товаров.”
    Да, это было бы идеально. Но этот вариант я сразу отбросила, потому что даже не представляю как это реализовать, кроме как в updater’е вызывать функцию, которая будет генерировать новый массив и передавать его в source. Есть подсказки? Как в typeahead отслеживать, что производитель выбран?

    Reply
    • Здесь можно попробовать несколько реализаций.
      1. Например (с разделителем – пробел, “,” и т.д.). В этом случае если сервер получает разделитель переносим поиск в следующую таблицу с учетом первого параметра.

      2. Ищем во всех таблицах, но выводим список по образцу, например, “производитель” + [“товар”], на самом деле товара может и не быть в начале.

      Пример вторго варианта на Kohana PHP:

      
      $query = $_REQUEST['query'];
      $result = array();
      
      //Получаем всех производителей в массив $result['id']['name']
      $result = ORM::factory('Manufacture')->where('name', 'like', '%'. 
      $query . '%')->find_all()->as_array('id', 'name');
      
      //Ищем товары
      $goods = ORM::factory('Good')->where('name', 'like', '%'. 
      $query . '%')->find_all();
      
      // Перебираем найденные товары и формируем строку состоящую из
      // имени производителя и названия товара (для этого в модели 
      // товаров необходимо описать отношение belongs_to между моделью 
      // товаров и моделью производителей)
      foreach($goods as $good)
      {
        $result[$good->manufacture->id] = $good->manufacture->name . 
      ' ' . $good->name;
      }
      
      //Выводим полученный результат
      echo json_encode($result);
      
      Reply
  4. А если я клонирую объект(), разумеется переопределяю все id внутр. эл-тов.
    Но после этого клон не работает.
    Это и понятно, $document отслеживает уже загруженный док.
    И как привести это все к конструкции
    $(‘body’).on(‘какой метод?’, ‘typeahead’, function(){

    });

    Reply
  5. $(‘body’).on(…) – on() – метод jquery
    $(‘input[name=username]’).typeahead(…) – typeahead() – метод bootstap
    Скорее всего вам надо смотреть в сторону динамического связывания typeahead() с новым объектом.
    Попробуйте, что-то вроде:
    $(‘input[name=username]’).clone().typeahead(…)

    Reply

Leave a reply

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

required