4.4.1. AjaxDropdownPreloader
Часто на форме редактирования возникает необходимость вывести два выпадающих меню (далее ВМ), где
набор опций второго ВМ напрямую зависит от выбранной в первом ВМ опции. Причем требуется динамическое
обновление опций зависимого ВМ без отправки (submit) формы на сервер. Вышеописанную функциональность
можно самостоятельно реализовать стандартными средствами HTML и Javascript, однако рекомендуется
использовать уже написанный JavaScript класс AjaxDropdownPreloader, который её реализует.
При изменении значения в главном ВМ, отсылается AJAX запрос
на сервер. Сервер выполняет необходимые расчёты и получает новый набор опций для подчиненного ВМ. Полученный
набор опций возвращается в виде XML документа
на страницу, которая послала AJAX запрос. Далее происходит обработка полученного XML документа и замена опций
подчиненного ВМ средствами JavaScript на стороне клиента.
Параметры инициализации
Для успешной реализации связи между двумя ВМ необходимо создать экземпляр класса AjaxDropdownPreloader, передав ему параметры, описанные в приведённой ниже таблице.
название |
описание |
|---|---|
| $url (string) | Ссылка, при заходе на которую будет возвращён XML документ, содержащий новый набор опций для зависимого ВМ. XML документ должен быть в следующем формате: <field_options>
<option>ID1</option>
<option>ID2</option>
</field_options>
Стоит обратить особое внимание на то, что XML документ содержит только ID опций (без текста, который будет виден в зависимом ВМ). Для того, чтобы у опций был и текст нужно изначально заполнить зависимый ВМ всеми возможными опциями. Обычно установка kOptionsFormatter форматера на это поле является вполне достаточным. |
| $input_mask (string) | Маска для получения любого элемента ввода на форме. Обычно маска получается путём
вызова тэга InputName со значением <inp2:prefix_InputName field="#FIELD#"/>
// вернёт строку вида: prefix[ID][#FIELD#]
|
| $filter_field (string) | Название поля главного ВМ. |
| $dependend_field (string) | Название поля подчиненного ВМ. |
| value (int) | Данный параметр позволяет указать на то, какое значение должно быть выбрано в зависимом ВМ после обновления его набора опций. Если его не передать, то автоматически будет выбрано значение, которое было выбрано до получения нового набора опций (только если оно в нём также присутствует). |
Настройка шаблона
Для примера рассматривается стандартная форма редактирования с двумя ВМ:
MainField- главное ВМ;DependentField- зависимое и ВМ.
При изменении выбранного значения в поле 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. Данный метод
рекомендуется переопределять, когда требуется выполнение специфической функциональности после выполнения
фильтраций опций зависимого ВМ.
См. также