# AutoComplete

Многие JavaScript-библиотеки для AJAX предоставляют функционал для автодополнения, с его помощью отображается список возможных соответствий в то время, пока пользователь набирает текст. Помощник `AutoComplete` предназначен для облегчения возврата допустимых ответов для функционала такого рода.

Поскольку не все JavaScript-библиотеки реализуют автодополнение одинаково, то помощник `AutoComplete` предоставляет в абстрактном классе некоторый базовый функционал, необходимый для большинства библиотек, и конкретные реализации для отдельных библиотек. Возвращаемые данные в основном - JSON-массивы строк, JSON-массивы массивов (в которых каждый массив-член является ассоциативным массивом метаданных, используемых при создании списка), либо HTML.

Базовое использование одинаково для всех реализаций:

    class FooController extends Zend_Controller_Action
    {
        public function barAction()
        {
            // Выполнение некоторой логики...

            // Кодирование и отправка ответа:
            $this->_helper->autoCompleteDojo($data);

            // То же самое явным образом:
            $response = $this->_helper->autoCompleteDojo
                                      ->sendAutoCompletion($data);

            // Или просто подготовьте ответ для автодополнения:
            $response = $this->_helper->autoCompleteDojo
                                      ->prepareAutoCompletion($data);
        }
    }

По умолчанию этот помощник делает следующее:

- Отключает макеты и ViewRenderer.

- Устанавливает необходимые заголовки ответа.

- Устанавливает тело ответа с закодированными данными в нужном формате для автодополнения.

- Отправляет ответ.

Доступные методы помощника включают в себя:

- `disableLayouts()` может использоваться для отключения макетов и ViewRenderer. Обычно он вызывается в `prepareAutoCompletion()`.

- `encodeJson($data, $keepLayouts = false)` будет кодировать данные в формат JSON, при этом можно опционально включать или отключать макеты. Обычно этот метод вызывается в `prepareAutoCompletion()`.

- `prepareAutoCompletion($data, $keepLayouts = false)` используется для подготовки ответа в формате, необходимом для конкретной реализации, при этом можно опционально включать или отключать макеты. Возвращаемое значение может варьироваться в зависимости от используемой реализации.

- `sendAutoCompletion($data, $keepLayouts = false)` используется для отправки данных в формате, необходимом для конкретной реализации. Он вызывает `prepareAutoCompletion()` и затем отправляет ответ.

- `direct($data, $sendNow = true, $keepLayouts = false)` используется, когда помощник вызывается как метод брокера помощников. Флаг `$sendNow` используется для определения того, какой метод вызывать - `sendAutoCompletion()` или `prepareAutoCompletion()`. По умолчанию ответ отправляется сразу.

В настоящее время `AutoComplete` поддерживает AJAX-библиотеки Dojo и Scriptaculous.

## Автодополнение c Dojo

В Dojo нет собственно виджета для автодополнения, но есть два виджета, которые могут производить автодополнение: ComboBox и FilteringSelect. В обоих случаях они требуют использования хранилища данных, который реализует QueryReadStore, читайте документацию по dojo.data для получения более подробной информации по этой теме.

В Zend Framework вы можете передавать простой индексный массив для помощника AutoCompleteDojo, и он будет возвращен в формате, пригодном для использования с хранилищем в Dojo:

    // в контроллере действий:
    $this->_helper->autoCompleteDojo($data);

### Автодополнение с Dojo и Zend MVC

Автодополнение с Dojo через Zend MVC требует реализации нескольких вещей: генерация объекта формы для того ComboBox, для которого нужно реализовать автодополнение, действие контроллера для обслуживания автодополнения, создание своего QueryReadStore для связи с этим действием и генерация javascript-кода для инициализации автодополнения.

Для начала рассмотрим, что нужно сделать по части javascript. Dojo представляет собой полный фреймворк для создания объектно-ориентированного кода на языке JavaScript, почти так же, как Zend Framework на языке PHP. Он позволяет создавать псевдопространства имен, используя для этого иерархию директорий. Создадим директорию 'custom' на том же уровне, что и директория Dojo. В этой директории создадим файл TestNameReadStore.js со следующим содержимым:

    dojo.provide("custom.TestNameReadStore");
    dojo.declare("custom.TestNameReadStore", dojox.data.QueryReadStore, {
        fetch:function (request) {
            request.serverQuery = { test:request.query.name };
            return this.inherited("fetch", arguments);
        }
    });

Этот класс просто наследует от класса QueryReadStore фреймворка Dojo, который сам по себе является абстрактным. Мы просто определяем метод, по которому производится запрос, и присваиваем его элементу 'test'.

Далее создадим элемент формы, для которого хотим реализовать автодополнение:

    class TestController extends Zend_Controller_Action
    {
        protected $_form;

        public function getForm()
        {
            if (null === $this->_form) {
                $this->_form = new Zend_Form();
                $this->_form->setMethod('get')
                    ->setAction(
                        $this->getRequest()->getBaseUrl() . '/test/process'
                    )
                    ->addElements(array(
                        'test' => array('type' => 'text', 'options' => array(
                            'filters'        => array('StringTrim'),
                            'dojoType'       => array('dijit.form.ComboBox'),
                            'store'          => 'testStore',
                            'autoComplete'   => 'false',
                            'hasDownArrow'   => 'true',
                            'label' => 'Your input:',
                        )),
                        'go' => array('type' => 'submit',
                                      'options' => array('label' => 'Go!'))
                    ));
            }
            return $this->_form;
        }
    }

Здесь мы просто создаем форму с методами 'test' и 'go'. Метод 'test' добавляет несколько специальных, специфических для Dojo атрибутов: dojoType, store, autoComplete и hasDownArrow. Через 'dojoType' мы указываем, что создается ComboBox, он связан с хранилищем данных (ключ 'store') 'testStore'. Устанавливая 'autoComplete' в false, мы говорим Dojo, чтобы он не выбирал автоматически первое соответствие, а вместо этого показывал список соответсвий. Наконец, 'hasDownArrow' создает стрелку наподобие той, что присутствует в выпадающем списке, чтобы можно было выводить и убирать список соответствий.

Добавим метод для отображения формы и действие для обработки автодополнения:

    class TestController extends Zend_Controller_Action
    {
        // ...

        /**
         * Страница с формой
         */
        public function indexAction()
        {
            $this->view->form = $this->getForm();
        }

        public function autocompleteAction()
        {
            if ('ajax' != $this->_getParam('format', false)) {
                return $this->_helper->redirector('index');
            }
            if ($this->getRequest()->isPost()) {
                return $this->_helper->redirector('index');
            }

            $match = trim($this->getRequest()->getQuery('test', ''));

            $matches = array();
            foreach ($this->getData() as $datum) {
                if (0 === strpos($datum, $match)) {
                    $matches[] = $datum;
                }
            }
            $this->_helper->autoCompleteDojo($matches);
        }
    }

В методе `autocompleteAction()` мы делаем несколько вещей. Сначала мы проверяем, был ли произведен POST-запрос и имеет ли параметр 'format' значение 'ajax', это помогает отсечь большинство ложных запросов. Далее мы получаем параметр 'test' и сравниваем его с нашими данными (здесь я намеренно опустил реализацию метода `getData()`, источники данных могут быть любыми). В конце выполнения передаем найденные соответствия помощнику AutoCompletion.

Теперь на очереди написание скрипта вида для страницы с формой. В нем нужно установить хранилище данных, вывести форму и подключить все необходимые библиотеки Dojo, включая наше хранилище данных. Ниже приведен скрипт вида с комментариями:

    <?php // установка хранилища данных: ?>
    <div dojoType="custom.TestNameReadStore" jsId="testStore"
        url="<?php echo $this->baseUrl() ?>/unit-test/autocomplete/format/ajax"
        requestMethod="get"></div>

    <?php // рендеринг формы: ?>
    <?php echo $this->form ?>

    <? // подключение CSS для Dojo в HTML-заголовке: ?>
    <?php $this->headStyle()->captureStart() ?>
    @import "<?php echo $this->baseUrl()
    ?>/javascript/dijit/themes/tundra/tundra.css";
    @import "<?php echo $this->baseUrl() ?>/javascript/dojo/resources/dojo.css";
    <?php $this->headStyle()->captureEnd() ?>

    <?php // подключение javascript в HTML-заголовке, включая все
       // необходимые библиотеки для Dojo: ?>
    <?php $this->headScript()
            ->setAllowArbitraryAttributes(true)
            ->appendFile($this->baseUrl() . '/javascript/dojo/dojo.js',
                'text/javascript',
                array('djConfig' => 'parseOnLoad: true'))
            ->captureStart() ?>
    djConfig.usePlainJson=true;
    dojo.registerModulePath("custom","../custom");
    dojo.require("dojo.parser");
    dojo.require("dojox.data.QueryReadStore");
    dojo.require("dijit.form.ComboBox");
    dojo.require("custom.TestNameReadStore");
    <?php $this->headScript()->captureEnd() ?>

Обратите внимание на вызовы помощников видов, таких, как headStyle м headScript, - это метки заполнения, которые могут затем рендериться в HTML-заголовке скрипта макета.

Теперь у нас есть всё для того, чтобы автодополнение с Dojo заработало.

## Автодополнение с Scriptaculous

Scriptaculous ожидает HTML-ответ в определенном формате.

С этой библиотекой используется помощник 'AutoCompleteScriptaculous'. Просто передавайте ему массив данных, и он создает ответ HTML, совместимый с Ajax.Autocompleter.