NewVision WEB-development Team Blog

Прикручиваем Autocomplete с изменяемой callback-функцией скрипта

Жил себе жил и тут вот резко захотелось прикрутить такую модно-прогрессивную штуку к нам в проект, как Autocomplete. И все бы хорошо, да вот есть у нас под строкой ввода поискослов два волшебных радиобатона. А переключают эти радиобатоны разные типы поиска. То-есть нажал на один, вводишь слово – выводится список слов выбранных с помощью одного скрипта, нажал другой и, о чудо, запрос убежал умчался к другому скрипту. Волшебство, магия и страшное колдунство, а делать-то надо, иначе не интересно жить:)

Поразмыслив решили сделать роут, куда будет обращаться скрипт, с передаваемым параметром. Для JS скрипта – это означало подменять все время адрес callback функции. По роуту мы передаем тип радиобатона, а потом в методе его разбираем. Соответственно value вышеозначенных radiobuttons должно быть названием типа.
Опишу, пожалуй, что ж я такого наделал, чтобы это все заработало. Есть такой замечательный виджет для Symfony, как sfWidgetFormJQueryAutocompleter. Вот от него и решил плясать. Понятно, что при каждом выборе радиобатона, должна меняться callback функция скрипта. Подменять адрес функции в стандартном автокомплитере не получалось, поэтому в панике и кипеше был срочно найден отличный аналог: Ajax Autocomplete for jQuery, от замечательного парня с не русской фамилией Tomas Kirda. Скачать можно не стесняясь прямо отсюда: http://www.devbridge.com/projects/autocomplete/jquery/.
Итак, мы скачали виджет, автокомплит, позакидывали по своим любимым папочкам. Ну, а теперь пора и начать писать кучу бесполезных символов. Сначала лезем в код самого autocomplete.js и внедряем в его недра, например после функции setOptions, нашу:

   setOptionsUrl: function(option){
      this.serviceUrl = option;
    },

Первый шаг завершен, все радуются, девушки плачут, парни завидуют. Внезапно! Акт второй. Лезем в наш формфильтр и для генерации формы поиска приписываем нечто похожее:

protected function setupRelevance()
  {
    $this->validatorSchema['search'] = new sfValidatorSchema(array(
      'key_words' => new sfValidatorString(array('max_length' => 255, 'min_length' => '0', 'required' => false)),
      'type'      => new sfValidatorChoice(array('choices' => array('Type1' , 'Type2'), 'required' => false)),
    ));
   
    $type_widget = new sfWidgetFormChoice(array(
          'expanded' => true,
          'choices'  => array('Type1' => "Search first", 'Type2' => "Search second"),
          'label'    => ' '
        ), array('class' => 'search_type_checkbox'));

    $this->setWidget('search', new nvWidgetFormSchema(
      array(
        'key_words' => new sfWidgetFormJQueryAutocompleterSimpleLsi(array(
          'url'       => $this->getOption('url'),
          'config'    => json_encode( array( 'multiple' => false)),
          'class'     => 'search_type_checkbox'
      )),
        'type' => $type_widget
      ),
      array('validator_schema' => $this->validatorSchema['search'])
    ));
  }

Надеюсь, тут все понятно. Не забываем при инициализации в action вашего фильтра, передать url на метод, который будет возвращать нам слова. Например это AjaxGetWords. И вот на него роут:

get_lexicon_words:
  url:   /words-ajax/:search_type
  param: { module: publisher_search, action: AjaxGetWords, search_type:null }

Лезем в свой модуль и начинаем судорожно сотворять нужный метод:

public function executeAjaxGetWords($request)
{
    $this->getResponse()->setContentType('application/json');

    $q       = $request->getParameter('query');
    $limit   = $request->getParameter('limit');
    $stype = $request->getParameter('search_type');
    $words = Doctrine_Core::getTable('Lexicon')->getAjaxWords($q, $limit, $stype);

    return $this->renderText(json_encode($words));
  }

Не забываем про модель. В нее мы передаем тип поиска (в зависимости от выбранного радиобатона), буквы, которые вбили (query) и количество возвращаемого результата.

Ну вот, осталось совсем немного. Еще чуть-чуть и будет вселенское счастье и поклеточный оргазм. Приведу целиком метод render класса sfWidgetFormJQueryAutocompleter :

public function render($name, $value = null, $attributes = array(), $errors = array())
  {
    $class = $this->getOption('class');
    $url   = $this->getOption('url');

    return $this->renderTag('input', array_merge(array('type' => $this->getOption('type'), 'name' => $name, 'value' => $value), $attributes)).
           sprintf(<<<EOF
<script type="text/javascript">
  \$(document).ready(function()
  {
    top.type = '';
    top.ac = '';
    top.type = '%s/' + $('input.%s:checked').val();
    top.ac = $('#%s').autocomplete(
    {
      serviceUrl:top.type,
      noCache: false,
      params: { limit:'10' } //aditional parameters
    });

    \$("input.%s").click(
      function() {
        top.type = '%s/' + $('input.%s:checked').val();
        top.ac.disable();
        top.ac.clearCache();
        top.ac.setOptionsUrl(top.type);
        top.ac.enable();
      }
    );
  });
</script>
EOF

      ,
      $url,
      $class,
      $this->generateId($name),
      $class,
      $url,
      $class
    );
    die;
  }

То-есть, при клике на один из радиобатонов вызываются функции автокомплитера в таком порядке: отключается, очищается кеш, устанавливаем урл (тот который мы передавали + тип), ну и, собственно, включаем обратно.
Вроде все, поставили, посмотрели, потыкали мышкой, порадовались. Самомнение подросло – можно идти довольным спать))

Оставить комментарий