K4:AjaxDropdownPreloader
From In-Portal Developers Guide
| ||
---|---|---|
Статьи в этой категории | ||
|
Часто на форме редактирования возникает необходимость вывести два выпадающих меню (далее ВМ), где набор опций второго ВМ напрямую зависит от выбранной в первом ВМ опции. Причем требуется динамическое обновление опций зависимого ВМ без отправки (submit
) формы на сервер. Вышеописанную функциональность можно самостоятельно реализовать стандартными средствами HTML
и Javascript
, однако рекомендуется использовать уже написанный JavaScript
класс AjaxDropdownPreloader
, который её реализует.
При изменении значения в главном ВМ, отсылается AJAX запрос на сервер. Сервер выполняет необходимые расчёты и получает новый набор опций для подчиненного ВМ. Полученный набор опций возвращается в виде XML документа на страницу, которая послала AJAX запрос. Далее происходит обработка полученного
XML документа и замена опций подчиненного ВМ средствами JavaScript
на стороне клиента.
Contents |
Параметры инициализации
Для успешной реализации связи между двумя ВМ необходимо создать экземпляр класса AjaxDropdownPreloader, передав ему параметры, описанные в приведённой ниже таблице.
название | описание |
---|---|
$url (string)
| Ссылка, при заходе на которую будет возвращён XML документ, содержащий новый набор опций для зависимого ВМ. XML документ должен быть в следующем формате:
<field_options> <option>ID1</option> <option>ID2</option> </field_options> Стоит обратить особое внимание на то, что XML документ содержит только ID опций (без текста, который будет виден в зависимом ВМ). Для того, чтобы у опций был и текст нужно изначально заполнить зависимый ВМ всеми возможными опциями. Обычно установка kOptionsFormatter форматера на это поле является вполне достаточным. |
$input_mask (string)
| Маска для получения любого элемента ввода на форме. Обычно маска получается путём вызова тэга InputName со значением "#FIELD# " в качестве названия поля объекта:
<inp2:prefix_InputName field="#FIELD#"/>
// вернёт строку вида: prefix[ID][#FIELD#] |
$filter_field (string) | Название поля главного ВМ. |
$dependend_field (string) | Название поля подчиненного ВМ. |
value (int) | Данный параметр позволяет указать на то, какое значение должно быть выбрано в зависимом ВМ после обновления его набора опций. Если его не передать, то автоматически будет выбрано значение, которое было выбрано до получения нового набора опций (только если оно в нём также присутствует). |
Настройка шаблона
Для примера рассматривается стандартная форма редактирования с двумя ВМ:
При изменении выбранного значения в поле MainField
срабатывает событие onchange
и через него подгружаются соответствующие опции в поле DependentField
. После выполнения приведённых ниже шагов настройку шаблона можно считать завершенной.
- Добавить элементы ВМ:
<inp2:m_RenderElement name="inp_edit_options" prefix="sample-prefix" field="MainField" title="la_fld_MainField"/> <inp2:m_RenderElement name="inp_edit_options" prefix="sample-prefix" field="DependentField" title="la_fld_DependentField"/>
- Создать экземпляр класса
AjaxDropdownPreloader
:
var DependentFieldPreloader = new AjaxDropdownPreloader( '<inp2:m_Link template="dummy" sample-prefix_event="OnQueryDependentXML" pass="m,sample-prefix" filter_value="#FILTER_VALUE#" no_amp="1"/>', '<inp2:sample-prefix_InputName field="#FIELD#"/>', 'MainField', 'DependentField' );
- Назначить событие
onchange
для главного ВМ:
addEvent(DependentFieldPreloader.getControl('MainField'), 'change', function() {DependentFieldPreloader.Query()});
- Отфильтровать значения для зависимого ВМ сразу после загрузки страницы.
Application.setHook('m:OnAfterWindowLoad', function () { DependentFieldPreloader.Query(); });
Весь приведённый выше JavaScript
код нужно писать после того, как на форме будут отображены элементы, с которыми он работает. Самое оптимальное для этого место перед подключением шаблона "incs/footer
". Это наглядно будет показано на ниже приведённом примере.
<script type="text/javascript"> // javascript code here </script> <inp2:m_include t="incs/footer"/>
В данном случае переданный шаблон "dummy
" использоваться не будет (его даже может не существовать), а вся подготовка XML документа будет происходить в событии OnQueryDependentXML
.
Настройка обработчика событий
В обработчик событий от префикса "sample-prefix
" необходимо добавить событие OnQueryDependentXML
(указанное на шаблоне редактирования), которое в результате своей работы будет возвращать в поток вывода (output stream) XML документ. Возвращаемый XML документ будет в последствии обрабатывается классом AjaxDropdownPreloader
и зависимое ВМ будет заполняется опциями на стороне клиента. После выполнения всех ниже приведённых шагов можно считать настройку обработчика событий завершённой.
Добавить тело события OnQueryDependentXML
в обработчик событий от префикса "sample-prefix
":
/** * [AJAX] Метод для получения отфильтрованных опций в виде XML документа. * * @param kEvent $event */ function OnQueryDependentXML(&$event) { $event->status = erSTOP; $filter_value = $this->Application->GetVar('filter_value'); if (!$filter_value || ($this->Application->GetVar('ajax') != 'yes')) { return ; } $sql = 'SELECT DependentTable.FieldId FROM DependentTable WHERE DependentTable.MainId = ' . $filter_value; $dependent_ids = $this->Conn->GetCol($sql); $xml = ''; foreach ($dependent_ids as $id) { $xml .= '<option>' . $id . '</option>'; } $xml = '<field_options>' . $xml . '</field_options>'; $this->Application->XMLHeader(); echo $xml; }
Событие OnQueryDependentXML
необходимо добавить в метод mapPermissions, который обеспечит проверку наличия у пользователя необходимых прав доступа для вызова данного события:
/** * Метод связывающий события и права, необходимые для их выполнения. * */ function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnQueryDependentXML' => Array ('self' => 'view'), ); $this->permMapping = array_merge($this->permMapping, $permissions); }
Также следует обратить внимание на некоторые, описанные ниже, приёмы, которые использовались при написании события OnQueryDependentXML .
|
- В начале события рекомендуется установить статус его выполнения в erSTOP. Это укажет на то, что по окончания выполнения события не нужно показывать содержание переданного шаблона (в данном случае это "
dummy
"):
$event->status = erSTOP;
- В начале события написать код, который позволит игнорировать запросы, которые будут делать поисковые системы:
if ($this->Application->GetVar('ajax') != 'yes') { return ; }
Параметр "ajax
" добавляется автоматически при отправлении каждого AJAX запроса. Если поисковая система где-то найдёт ссылку, в которой указано данное событие, то зайдя на неё тело события выполнено не будет.
- Перед выводом XML документа на экран необходимо послать браузеру соответствующий заголовок. Сделать это можно при помощи метода
Application::XMLHeader
:
$this->Application->XMLHeader();
Конечно такой заголовок слать не нужно, если не планируется возвращать XML документ. |
Использование метода AfterProcess
В классе AjaxDropdownPreloader
также доступен абстрактный метод AfterProcess
. Данный метод рекомендуется переопределять, когда требуется выполнение специфической функциональности после выполнения фильтраций опций зависимого ВМ.