Запись была отнесена к рубрике «ExtJS» 14.03.2008, Пт в 05:43. Вы можете следить за общением по теме этой записи с помощью RSS 2.0 ленты. Вы можете оставить отзыв, или trackback с Вашего сайта.
Многие проекты на данный момент используют информацию о местонахождении своих клиентов. К таким относятся интернет-магазины, сайты знакомств, банковские операционные ресурсы и прочее. Именно об элементе указания такого рода информации и будет данная статья: Ext.ux.locationSelect реализованный в поле фреймворка ExtJS 2.
Маленькая демка поможет ответить на вопрос о необходимости вчитываться в дальнейшее.
Так сложилось исторически, что управляющим элементом по выбору локации (будем пользоваться этим словом для определения месторасположения, местонахождения и иного) является некоторое количество взаимосвязанных списков <select>, позволяющих последовательно уточнять локацию часть за частью. Выглядеть это может примерно следующим образом. Контрол в сумме удобен, малопротиворечив, но несколько устарел. Вот первые, бросающиеся в глаза, минусы решения:
В одном из проектов мне понадобилось обойти всё вышеперечисленное и ко всему прочему соблюсти следующее:
Нам потребуется:
Использование ExtJS обусловлено требованием №1 — интерфейс window based. Только этот фреймворк способен был справиться со всеми требованиями, которые были предъявлены к процессу работы с данными.
ZF и базирующийся на его составляющих Application_Db_Table_Nestedset это ответ на вопрос стандартизации кода, ответ в ряде мест противоречивый, но все же ответ.
данные ZF-элементы дизайна контрола можно исключить и заменить на что-либо более привычное буквально в течении получаса.
Использование, благодаря мастерству и прозорливости разработчиков ExtJS, практически ничем не отличается от использования стандартных компонент этого пакета — создать и применить Ext.ux.locationSelect также легко как и создать обычную панель.
Подключение реализуется в обычном порядке. Если ExtJS уже используется, то необходимо подключить только само расширение:
<head>
<script type="text/javascript" src="/lib/ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="/lib/ext/ext-all.js"></script>
<script type="text/javascript" src="/lib/ext/ux/locationSelect.js"></script>
<link href="/lib/ext/resources/css/ext-all.css" rel="stylesheet" type="text/css" />
</head>
используйте правильный DOCTYPE документа, часто многие нетривиальности можно решить раз и навсегда только начав работать в правильном режиме.
Ввиду того, что контрол расширяет Ext.form.FieldSet, то видеть себя он предполагает в поле Ext.FormPanel, однако это необязательное требование.
подробнейшее описание API, конфигураций и немного примеров по каждому из контролов можно обнаружить в ExtJS API Documentation в соответствующей части дерева компонент, пакетов и классов.
Таким образом включить контрол в форму можно простым добавлением его конфигурационного объекта в items формы:
var myForm = new Ext.form.FormPanel({
items: [{
xtype: 'locationselect',
url: '/someURL',
prefix: 'some_location_',
title: 'someFieldSetTitle',
valueNotFoundText: 'Не важно',
validator: function(){/*some js-code*/}
autoHeight: true
}]
});
Нестандартными конфигурационными полями являются:
обратите внимание на символ подчеркивания в именах генерируемых по префиксу переменных. Дело в том, что комбобокс в ExtJS реализации состоит из двух полей ввода — одно из которых и отправляет значение при сабмите формы т.е. приходится генерировать два похожих имени для каждого комбобокса.
Все остальные поля — наследие конфигурации суперкласса.
Существуют задачи когда необходимо позволить выбирать локацию и при этом начать выбор с той, что уже установлена — редактирование анкетных данных, оформление переезда и прочее.
Для этой цели контрол имеет метод loadLocation (), который принимает конфигурационный объект формата {country: integer, region: integer, city: integer}. При его вызове будет произведено обращение по url, указанном при создании, с передачей параметров локации. Если вернувшиеся данные соответствуют допустимому формату, то в соответствующие комбобоксы будут загружены списки с данными, а те части локации, которые были заданы в конфигурационном объекте будут выбраны.
Так как контрол подкачивает данные с помощью AJAX, необходимо «договориться» о протоколе общения его с сервером. Здесь имеется развилка:
Мне было удобнее реализовать обе подгрузки в одном действии locationSelectGetSublocations () контроллера AjaxController.
public function locationSelectGetSublocationsAction() {
$this->_helper->viewRenderer->setNoRender();
$filter = new Zend_Filter_Digits();
$location = new Location();
if ($this->getRequest()->getParam('country', 0) && $this->getRequest()->getParam('region', 0)){
$countryId = $filter->filter($this->getRequest()->getParam('country', 0));
$regionId = $filter->filter($this->getRequest()->getParam('region', 0));
$result = array(
'region' => $location->getSublocations($countryId),
'city' => $location->getSublocations($regionId)
);
//добавляем опции по умолчанию
array_unshift($result['region'], array('id' => 0, 'name' => 'Не имеет значения'));
array_unshift($result['city'], array('id' => 0, 'name' => 'Не имеет значения'));
echo json_encode(array('rows' => $result));
} else {
$id = $filter->filter($this->getRequest()->getParam('parentId', 1));
$result = $location->getSublocations($id);
//добавляем опцию по умолчанию
array_unshift($result, array('id' => 0, 'name' => 'Не имеет значения'));
echo json_encode(array('rows' => $result));
}
}
вы можете не использовать ZF, хранить данные локаций в любом удобном для вас виде и делать, что заблагорассудится, главное — возвращать данные как это указано выше.
Белым пятном действия является класс Location — модель таблицы location. Это ничто иное как наследник класса Application_Db_Table_Nestedset о котором велась речь в предыдущей статье. Код модели имеет вид:
class Location extends Application_Db_Table_Nestedset{
protected $_name = 'location';
protected $_primary = 'id';
/**
* Return child locations of location
*
* @param integer parent location id
* @return array location items
*/
public function getSublocations($id){
$result = array();
foreach ($this->getChildren($id) as $row)
$result[] = array('id' => $row['id'], 'name' => $row['name']);
return $result;
}
}
Таблицу локаций было решено вынести в отдельный пункт из-за самой её сути. В свое время пришлось попотеть, чтобы найти в Сети довольно полные данные по странам, регионам и городам. Теперь, когда эта задача решена можно скачать порядка 20к объектов одним кликом.
Локации упорядочены и собраны в одной таблице по схеме Вложенных Множеств. Лично мне кажется, что эта схема является очень удачной для такого рода задач:
Пару часов назад я несколько обновил логику кеширования: теперь при создании контрола можно передать конфигурационный параметр «cache» с переменной, глобальной относительно контрола, где и будут храниться кешируемые данные (следствие несомненного плюса того, что переменные в JS передаются по ссылке). Таким образом достигается еще большая экономия запросов, наиболее ощутимая с ростом числа контролов на странице. В случае же, если используются два разных источника данных, «cache» в конфигурации можно опустить и контрол создаст кеш локальный.
[...] Если на ее обозрение имеется желание, то это можно сделать в индивидуальном [...]
Еще одно изменение, и довольно большое, было внесено в идею конфигурирования комбобоксов. Теперь контрол принимает объект, который может содержать индивидуальные настройки комбобоксов. Более подробно в . также отражает изменения.
просьба скинуть на почту исходники демки — так удобнее разбираться и адаптировать под себя
не хватает квалификации по кускам кода повторить функционал )))
спасибо за контрол!
У меня передается в параметере только
_dc 1257063754550
parentId 9
xaction load
как мне определить какую конкретно таблицу загружать?
данные передаются методом _GET, как я понимаю, должны передаваться методом _POST
Серверную логику контрол не реализует. Запросы, что не принципиально, типа POST. Т.е. это уже вам решать в каком виде хранить данные и как к ним/их обращаться/отдавать.
правильно ли я понимаю, вот я выбираю из списка регион, посылаются данные на сервер. правильно?
на серверной части (например в php файле) по какому параметру мне определить, что нужно прочитать базу со списком регионов или городов? какой параметр в методе ($_GET или $_POST) показывает, что было выбрано? просто я вижу только параметр parentId и как с одним параметром мне выбрать из базы город, где нужно как минимум два параметра — id города и id региона?
Верно, алгоритм работы такой же как и у пользователя — последовательно выбирать населенные пункты, уточняя их до требуемого.
Поглядите по реализации класса дерева вложенных множеств для Zend Framework. В ней есть ссылка на теоретическое описание почему достаточного одного ID узла чтобы выбрать всех его предков. Т.е. если хранить страны->регионы->города->улицы в виде узлов дерева связанных отношением родства, достаточного одного ID.