Нужно сделать форму поиска с динамически зависимыми полями. Как на авито, например. Т.е. выбираешь, например, категорию авто - тут же асинхронно появляются другие поля - новые авто, подержанные авто и т.д. Выбираешь из этих полей новые авто - асинхронно добавляются еще штук 5 полей, это могут быть марка, модель и т.д. Глубина вложенности неограниченная, но реально не более 5. Что есть сейчас. Есть 2 таблицы. Код (Text): CREATE TABLE `tbl_option` ( `opt_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `opt_value` INT(11) UNSIGNED NOT NULL, `opt_name` VARCHAR(128) NOT NULL, `opt_parent` INT(11) UNSIGNED NOT NULL, `opt_cat_id` INT(11) UNSIGNED NOT NULL, `opt_order` INT(4) UNSIGNED NOT NULL, PRIMARY KEY (`opt_id`) ) INSERT INTO `tbl_option` (`opt_id`, `opt_value`, `opt_name`, `opt_parent`, `opt_cat_id`, `opt_order`) VALUES (1, 1, 'Марка', 0, 2, 1); INSERT INTO `tbl_option` (`opt_id`, `opt_value`, `opt_name`, `opt_parent`, `opt_cat_id`, `opt_order`) VALUES (2, 2, 'Модель', 1, 2, 2); INSERT INTO `tbl_option` (`opt_id`, `opt_value`, `opt_name`, `opt_parent`, `opt_cat_id`, `opt_order`) VALUES (3, 3, 'Год выпуска', 0, 2, 3); INSERT INTO `tbl_option` (`opt_id`, `opt_value`, `opt_name`, `opt_parent`, `opt_cat_id`, `opt_order`) VALUES (4, 4, 'Зависит от модели', 2, 2, 4); Это таблица содержит id опций, их название, id родителей, которым они принадлежат. opt_cat_id - это идентификатор категорий авто и транспорта. А это - то какие значения они могу принимать. Все это option'ы. Код (Text): CREATE TABLE `tbl_dataset` ( `ds_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `ds_value` INT(11) UNSIGNED NOT NULL DEFAULT '0', `ds_name` VARCHAR(128) NOT NULL DEFAULT '0', `ds_option_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', `ds_parent` INT(11) UNSIGNED NOT NULL DEFAULT '0', `ds_order` INT(11) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`ds_id`) ) INSERT INTO `tbl_dataset` (`ds_id`, `ds_value`, `ds_name`, `ds_option_id`, `ds_parent`, `ds_order`) VALUES (1, 1, 'AC', 1, 0, 1); INSERT INTO `tbl_dataset` (`ds_id`, `ds_value`, `ds_name`, `ds_option_id`, `ds_parent`, `ds_order`) VALUES (2, 2, 'Acura', 1, 0, 2); INSERT INTO `tbl_dataset` (`ds_id`, `ds_value`, `ds_name`, `ds_option_id`, `ds_parent`, `ds_order`) VALUES (3, 3, 'Alfa Romeo', 1, 0, 3); INSERT INTO `tbl_dataset` (`ds_id`, `ds_value`, `ds_name`, `ds_option_id`, `ds_parent`, `ds_order`) VALUES (4, 4, 'Ace', 2, 1, 1); INSERT INTO `tbl_dataset` (`ds_id`, `ds_value`, `ds_name`, `ds_option_id`, `ds_parent`, `ds_order`) VALUES (5, 5, 'Acece', 2, 1, 2); INSERT INTO `tbl_dataset` (`ds_id`, `ds_value`, `ds_name`, `ds_option_id`, `ds_parent`, `ds_order`) VALUES (6, 6, 'Cobra', 2, 1, 3); INSERT INTO `tbl_dataset` (`ds_id`, `ds_value`, `ds_name`, `ds_option_id`, `ds_parent`, `ds_order`) VALUES (7, 7, 'принадлежит марке Acura', 2, 2, 1); INSERT INTO `tbl_dataset` (`ds_id`, `ds_value`, `ds_name`, `ds_option_id`, `ds_parent`, `ds_order`) VALUES (8, 8, 'Значение опции - только для модели Acece', 4, 5, 1); ds_option_id - указывает какой опции opt_parent из первой таблице принадлежит запись. ds_parent - говорит о том, что это значение принадлежит значениям ds_id. Например, марка AC имеет модели Ace, Acece, Cobra. При изменении значения форма сериализуется и POSTом отправляется в контроллер. Там обрабатывается и возвращается уже html-код, который вставляется в DIV. Это все получается, но не очень правильно. Это сама форма Код (Text): <script type="text/javascript"> jQuery(function($) { $('body').on('change','.opt', function() { //alert($('#search-model').serialize()); jQuery.ajax({'type':'POST','dataType':'json','data':$('#search-model').serialize(),'success':function(data){ if(true){ $('#dyn-flds').html(data.res); $('#v').html(data.res1); }else{ } },'url':'/index.php/site/search','cache':false});return false; }); }); </script> <div id="v"></div> <div class="form-actions" style=""> <?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array( 'id'=>'search-model', 'type'=>'horizontal addcomment-form', )); ?> <div> <?php echo $form->labelEx($model, 'cat'); ?> <?php $cat_data = Cat::model()->findAll(); $cat_data = CHtml::listData($cat_data, 'cat_value', 'cat_name'); ?> <?php echo $form->dropDownList($model, 'cat', $cat_data, array('class'=>'opt')); ?> <?php echo $form->error($model, 'cat'); ?> <div id="dyn-flds"> </div> </div> <?php $this->endWidget(); ?> </div><!-- form --> А это функция, которая занимается асинхронной обработкой Код (Text): public function actionSearch() { $search = $_POST['SearchForm']; $option_value = $_POST['Option']; if(count($option_value)) foreach($option_value as $o_key => $o_val) { $d[] = $o_key."->".$o_val; } if(count($d)) { $d = implode(", ", $d); } $options = Option::model()->findAll('opt_cat_id = '.$search['cat']); $this->options = $options; $this->option_value = $option_value; foreach ($options as $o) { foreach($o->dataset as $d) //$o->dataset - массив объектов значений для тек. опции { if($option_value[$o->opt_value] == $d->ds_parent) { $id = $d->ds_value; $parent_id = $d->ds_parent; $this->data[$id] = $d; $this->index[$parent_id][] = $id; $data .= $d->ds_value."<br>"; } } } foreach($options as $o) { $arr = null; if($o->opt_parent == 0) { $arr = CHtml::listData($o->dataset, 'ds_value', 'ds_name'); $data .= CHtml::dropDownList('Option['.$o->opt_value."]", $option_value[$o->opt_value], $arr, array('prompt'=>$o->opt_name, 'class'=>'opt', 'cat'=>$o->opt_cat_id)); } else { $dataset = $o->dataset; if(count($dataset)) $dd = null; foreach ($dataset as $d) { if($option_value[$o->opt_parent] == $d->ds_parent) { $dd[$d->ds_value] = $d->ds_name; } } if(count($dd)) { $data .= CHtml::dropDownList('Option['.$o->opt_value."]", $option_value[$o->opt_value], $dd, array('prompt'=>$o->opt_name, 'class'=>'opt')); } } } echo CJSON::encode(array('res'=>$data, 'res1'=> $d)); } При выборе марки - появляются модели. Тут все ок. При выборе модели ничего не происходит, за исключением когда выбрана Acece - тогда появляется доп. поле Зависит от модели. Т.е. при выбранyой марке AC и модели Acece есть поле Зависит от модели. И вот теперь если сменить марку, тол это поле останется, а должно пропасть. Это из-за того, что когда сбрасывается марка и форма отправляется, то модель-то остается и в общем я себе уже всю голову сломал. Очень нужна Ваша помощь. 3 дня уже туплю - ничего придумать не могу. PS Все делается на Yii, но это не суть важно. проблема в алгоритме.
Это потому что не хотите упростить себе задачу и стараетесь удержать в голове информацию о селектах и форме. Задачи нужно упрощать. Абстрагируйтесь от ваших марок, моделей и селектов html-формы. Ваша задача сводится, банально, к визуализации Nested Sets или вложенных множеств. Забудьте про формы и представьте что вы строете меню неограниченной вложенности. У вас есть сущность пункта меню, которая имеет связи с родительским и дочерними элементами (при необходимости ещё и уровень вложенности для быстрого поиска по дереву). Ваша задача сводится к получению ветки дерева, которая выше выбранного узла ветки и листочков, которые ниже него на один уровень (дочки первого уровня). Все остальные ветки и листья не относящиеся к вашему листочку должны быть удалены. При выборе конкретного узла дерева вы должны: 1. получить всех его родителей и их дочек первого уровня вложенности 2. получить дочек вашего листочка первого уровня вложенности. 3. убрать все остальные элементы дерева не относящиеся к выборке Теперь вернёмся в вашу практическую плоскость. Действия приложения должны быть примерно такими: 1. Передаём выбранный id поля в контроллер, получая структурированный массив полей (из бд или кэша) 2. Проверяем наличие этих полей в DOM, рендерите те, которых нет и делаете remove всех остальных. 3. Не забываем продумать кэширование, правильный формат передачи данных клиенту, возможное хранение всех полей в сессиях или jsone на клиенте (почему бы и нет: всё зависит от конкретной реализации. Если их 2-3 десятка и они не заводятся "на лету", то смысла мучить сервер нам нет). Передавать по ajax htmlи при визуализации любых вложенных множеств, имхо, не по феншую.
Эмм..а вы предлагаете генерить полный набор и отсекать лишнее перед отдачей клиенту? Сдается мне, это не совсем рилтаймовое решение.
Кто-нибудь решал подобную задачу? Я имею в виду реально, а не теоретически. Там не все так просто(по крайне мере для меня).
Нет. Я против именно использования html для транспортных задач, а не риалтайма и ajax-запросов. Передавал бы в jsone и рендерил бы изменяющиеся ветки дерева js-oм на клиенте. Против именно потому, что мы строим вложенные множества. Если использовать для передачи этих структур от сервера-клиенту html (веду речь не о начальной загрузке страницы а о запросах на дополнительные элементы дерева), то html должен будет содержать лишний код, ответственный за позиционирование передаваемого элемента множества. А то, какую часть дерева передавать по ajax-запросам: всю или видимую, или вообще передавать только id-шники которые нужно скрыть и добавить, а всё дерево изначально держать в jsone на клиенте с момента загрузки страницы — это уже субъективные детали которые от конкретного проекта зависят. Мы же с вами не знаем какое количество полей, их вложенность и изменчивость при каждом выборе узла дерева. Как удобнее так и можно делать. Удобнее передавать готовый html чтобы не возится с разбором в html jsona — ради Бога, можно и так. Тов. Swapf, вы или невнимательно читали или не вникли в суть поста. Вашу задачу решали практически все программисты. Любой кто имел дело с визуализацией вложенного меню на JS. Отличай заключается лишь в получении узлов дерева от сервера по xmlhttprequest. То, как должна быть решена задача вам объяснили. Если вы хотите само решение задачи для коммерческого проекта за вас, то так и нужно формулировать свой пост: "сделайте за меня то-то и то-то..". Что не просто или что вам осталось непонятно из описанного алгоритма действий?
Теперь понял. Ну, разница, по факту, лишь в количестве передаваемых данных, которых, на деле, вряд ли будет очень много. Даже по трудозатратам одинаково. Что тянуть данные из БД и строить HTML в пхп, чтобы отдать клиенту, что из тех же данных, обернутых в JSON строить HTML в браузере. Единственное, что в случае с генерацией на стороне сервера можно предусмотреть грейсфул деградейшн, если оказалось, что браузер не может в JS. Тогда сервер сможет отдать полностью готовую анкету. Да, там будут "избыточные" поля, которые придется оставить пустыми в ряде случаев, но, зато, будет работать.
вешаешь на ончендж или типа того обработчик. в обработчике смотришь значение или какой-нить дата-атрибут того, чего выбрали. делаешь запрос аяксом. получаешь ответ. пихаешь в нужном виде в нужное место. соотв обработчик можно навесить так, чтобы он динамически подцепился на новый хтмл код.
Коллеги, поправьте меня если я ошибаюсь (разработка под web не родная стезя), но, насколько я помню, поддержка xmlhttprequest в браузерах появилась позже поддержки работы с форматом json и функциями управления элементами DOM, которые используются для решения задачи. Если полностью отказываться от запросов к серверу за узлами дерева и отдавать все структурированные множества сразу может получиться слишком большой избыток лишних данных. Если к примеру мы ведём речь о 200 полях формы (не рассуждения о сферическом коне, буквально недавно был проект по недвижимости именно с такой задачей) из которых активных (выбранных пользователем) лишь 10, то остальные данные будут всё-таки лишними. А ведь поля могут быть ещё и с данными... . Как для хранения данных (даже если отдаём клиенту всё дерево при запросе страницы разом) так и для аякс-запросов я сторонник использования именно json потому что: 1. MVC-фетиш. Представление запрашивает у контроллера данные и должно получить данные, а не трусы в горошек. 2. Данные в json всё-таки заметно легче 3. Данные структурированы и ими легко оперировать если у вас предвидятся с ними дальнейшая динамика на стороне клиента. 4. Ну и приятнее поддерживать приложения в которых контроллеры не занимаются генерацией хтмелей. Тем более если тс делает проект на прогрессивном фреймворке. Другой разработчик который потом будет работать с его приложением ему же за это спасибо скажет.
хтмл отдавать или данные для генерации хтмл на клиенте - зависит только от того, что удобнее: данные или хтмл =)
Так то оно так, но вот лежит у меня файлик с jsonoм как раз подобных задаче полей формы из отраслевого портала. Весом 500 кб и я подозреваю, что разница с html-версией будет весьма значительной =) При этом, файлик то у юзеров в кэше, лежит никого не трогает, и для клиентского интерфейса нам даже не нужны дополнительные аяксы. А на бэкенде-то под них запрос на 3 таблицы: попроси субд, да сшей, да отдай и это ещё без прочих запросов для странички, которые СУБД обслуживает. Мне и коллеги и руководитель проекта до такой реализации говорили "Диман, ты заморачиваешься, и так работает", но это пока запросы к личному кабинету на создание нод не стали поступать сотнями. В общем всё субъективно, главное помнить что где уместно =)
Я не зря сказал про "грейсфул деградейшн". Суть этого подхода в том, чтобы "если что-то, по каким-то причинам, не может работать как надо, пусть работает хоть как-нибудь, лишь бы работало".