In-Portal Developers Guide

This is a wiki-based Developers Guide for In-Portal Open Source CMS. The purpose of this guide is to provide advanced users, web developers and programmers with documentation on how to expand, customize and improve the functionality and the code the In-Portal software. Please consider contributing to our documentation writing effort.

K4:AjaxDropdownPreloader

From In-Portal Developers Guide

Jump to: navigation, search
JavaScript классы JavaScript классы
Статьи в этой категории

Часто на форме редактирования возникает необходимость вывести два выпадающих меню (далее ВМ), где набор опций второго ВМ напрямую зависит от выбранной в первом ВМ опции. Причем требуется динамическое обновление опций зависимого ВМ без отправки (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 - главное ВМ;
  • 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);
}
Image:Infobox Icon.gif Также следует обратить внимание на некоторые, описанные ниже, приёмы, которые использовались при написании события OnQueryDependentXML.
  • В начале события рекомендуется установить статус его выполнения в erSTOP. Это укажет на то, что по окончания выполнения события не нужно показывать содержание переданного шаблона (в данном случае это "dummy"):
$event->status = erSTOP;
  • В начале события написать код, который позволит игнорировать запросы, которые будут делать поисковые системы:
if ($this->Application->GetVar('ajax') != 'yes') {
	return ;
}

Параметр "ajax" добавляется автоматически при отправлении каждого AJAX запроса. Если поисковая система где-то найдёт ссылку, в которой указано данное событие, то зайдя на неё тело события выполнено не будет.

  • Перед выводом XML документа на экран необходимо послать браузеру соответствующий заголовок. Сделать это можно при помощи метода Application::XMLHeader:
$this->Application->XMLHeader();
Image:Tipbox Icon.gif Конечно такой заголовок слать не нужно, если не планируется возвращать XML документ.

Использование метода AfterProcess

В классе AjaxDropdownPreloader также доступен абстрактный метод AfterProcess. Данный метод рекомендуется переопределять, когда требуется выполнение специфической функциональности после выполнения фильтраций опций зависимого ВМ.

См. также