УрокПерезагрузка форм на AJAX в Drupal 7
В Drupal 7 работа с AJAX в формах (и не только) сделана на порядок мощнее, чем в 6й версии ядра. Добавились многочисленные , расширились возможности его интеграции с разными элементами сайта. Но сегодня я решил написать пример интеграции форм с ajax через Forms API, тем более что совсем недавно я с этим столкнулся лично.
В моём примере данные загружаются динамично, поэтому возможность перезагружать формы по частям в Drupal 7 оказалась как нельзя кстати.
Постановка задачи
Имеется форма с двумя выпадающими списками и кнопкой. Первый список содержит все типы материалов на сайте. Второй список - последние 10 материлов из выбранного типа в первом списке. При выборе другого типа материала во второй список должны загружаться ноды этого типа. При нажатии на кнопку должно загружаться полное представление материала (такое же, как на странице node/номер). Всё это должно работать без перезагрузки страницы. Рисунок для наглядности:

Решение
Естественно, я сразу взялся за написание модуля, который я обозвал form_ajax. Итак, по порядку:
form_ajax.info
name = Form AJAX example description = Example of AJAX Forms API in Drupal 7 version = VERSION core = 7.x
Здесь ничего особенного - обычное описание модуля.
form_ajax.module
<?php /** * Implements hook_menu(). */ function form_ajax_menu() { $items = array(); $items['ajax_page'] = array( 'title' => t('Ajax form example'), 'page callback' => 'drupal_get_form', 'page arguments' => array('form_ajax_form'), 'access callback' => TRUE, 'file' => 'form_ajax.forms.inc', ); return $items; } /** * Get last 10 nodes from selected node type */ function form_ajax_get_last_nodes($type, $amount = 10) { // Загружаем из базы $amount последних материалов. Здесь никакой магии не происходит. $result = db_query_range('SELECT nid, title FROM {node} WHERE type = :type', 0, $amount, array(':type' => $type)); $nodes = array(); foreach ($result as $node) { $nodes[$node->nid] = check_plain($node->title); } return $nodes; }
Функция form_ajax_menu() добавляет на сайт страницу ajax_page, на которой будет располагаться моя форма.
Функция form_ajax_get_last_nodes() возвращает массив из последних нод определённого типа материала. Она понадобится нам чуть дальше.
form_ajax.forms.inc
Магия находится здесь. Все комментарии непосредственно в самом коде.
<?php /** * Form with AJAX example */ function form_ajax_form($form, &$form_state) { // Загружаем все существующие типы материала $options = node_type_get_names(); // Создаём в форме выпадающий список, элементами которого являются типы материалов. $form['type'] = array( '#type' => 'select', '#title' => t('Node types'), '#options' => $options, // Здесь подключаем ajax. При выборе элемента из выпадающего списка данные формы // будут переданы в функцию form_ajax_form_load_nodes(). Отработав, функция вернёт // данные, которые будут помещены в <div id = "form-ajax-nodes"></div>, заменив при этом // все данные, которые до этого там были (чтобы не плодить большое количество данных на странице). // В нашем случае функция вернёт эту же форму, но с выбранными элементами. '#ajax' => array( 'callback' => 'form_ajax_form_load_nodes', 'wrapper' => 'form-ajax-nodes', 'method' => 'replace', 'effect' => 'fade', ), ); // Если сработал AJAX - в $form_state будут переданы все значения формы. // Поэтому здесь проверка - если сработал AJAX - то выбранный тип материала берётся из // отправленной формы. А если не сработал - то тип будет первым элементом массива всех типов материалов. if (isset($form_state['values']['type'])) { $type = $form_state['values']['type']; } else { $type = array_shift($options); } // Устанавливаем значение по умолчанию для выпадающего списка с типами материалов. $form['type']['#default_value'] = $type; // Загружаем 10 последних материалов выбранного типа материала. // Здесь ключом является nid ноды, а значением - её заголовок. // Так же оборачиваем этот элемент в div, в который будет помещаться // обновлённая часть формы $options = form_ajax_get_last_nodes($type); $form['node'] = array( '#type' => 'select', '#title' => t('Nodes from !type type', array('!type' => $type)), '#options' => $options, '#prefix' => '<div id = "form-ajax-nodes">', '#suffix' => '</div>', ); // Кнопка, при нажатии на которую будет отправлен AJAX запрос, обрабатываемый // функцией form_ajax_form_load_node_content(). Она вернёт полное содержимое выбранной ноды // и поместит его в <div id = "form-ajax-node-content"></div>, заменив предыдущее содержимое, // если оно было. $form['submit'] = array( '#type' => 'submit', '#value' => t('Load content'), '#ajax' => array( 'callback' => 'form_ajax_form_load_node_content', 'wrapper' => 'form-ajax-node-content', 'method' => 'replace', 'effect' => 'fade', ), ); // Создаём в форме элемент страницы, куда будет помещено полное отображение ноды $form['markup'] = array( '#prefix' => '<div id = "form-ajax-node-content">', '#suffix' => '</div>', '#markup' => '', ); return $form; } /** * AJAX callback for loading node list */ function form_ajax_form_load_nodes($form, $form_state) { // Возвращаем элемент формы, который должен быть перезагружен. // В данном случае надо перезагрузить выпадающий список с материалами. return $form['node']; } /** * Return full view of selected node */ function form_ajax_form_load_node_content($form, $form_state) { // Возвращаем элемент формы, который должен быть перезагружен. // В данном случае надо перезагрузить элемент, который содержит представление ноды. if (isset($form_state['values']['node'])) { // Если есть в форме выбранный материал - загружаем его, // загружаем полное представление ноды, и отдаём отрендереное представление в качестве элемента формы. $nid = $form_state['values']['node']; $node = node_load($nid); $view = node_view($node); $form['markup']['#markup'] = render($view); } return $form['markup']; }
Если убрать комментарии - код выглядит гораздо менее устрашаюшим и меньшим по размеру :)
В итоге получается вот такая страница (с загруженным контентом):
- SplasH
- 17.10.2011
- 3592

Комментарии
Типы материала для select лучше делать при помощь node_type_get_names() (без цикла)
Уху, поправил.
Подскажите пожалуйста как все это установить на друпал. Модуль вроде подключился... а вот страницу не нахожу.
Перейдите на страницу /ajax_page
в том то и дело, что пишет "Страница не найдена
Страница "/eurotyre.com.ua/ajax_page" не найдена." у меня друпал на в корне сайта установлен.
Если модуль установлен - страница должна была создаться, по другому не может быть.
модуль установлен, но страницу не находит почему то. Drupal 7.9.
Можно сайт посмотреть в интернете? Я в это не верю)
http://pilipok.ru/eurotyre.com.ua/ajax_page вот. модуль точно установлен.

Очень странно. Кэш на сайте сбрасывали?
Да, сбрасывал. Тут еще такое дело... у меня не создается страница для моей настройки... в одном из модулей.
В вообще вопрос был у меня такой... у меня есть фильтр, который реализован в виде отдельного скприпта. Но этот скрипт выводит результат работы в на отдельную страницу. Хотел объединить резельтат его работы и view фильтрацию для магазина.
Поставил на другом сайте. На сайте Drupal 7.9? но без интернет магазина... видимо какой глюк одном из моделей... который все портит....
ссылка Так что пример работает. Извините за беспокойство... хотя мне другое немного надо было сделать....
Без проблем. А что вам именно надо сделать?
Огромное Вам спасибо. Очень классный у Вас пример! Действительно работает! Очень прозрачный и понятный!
Сделать нужно поиск нодов и их отображение. Уже сделал по образу и подобию Вашей формы, форму выбора условий, но вопрос в том, как отобразить больше одной ноды? У вас отображается только одна. Попробовал выводить через рендеринг 2 ноды, но не очень красиво выходит. Можноли как то через view отобразить несколько нод?
Чтобы вывести данные через вьюс - создайте на сайте вьюху, которая выводит ноды, а потом просто вместо node_view($node) передайте views_embed_view с параметрами. (http://www.drupal.ru/node/29439 первый коммент)
Если честно. Непонятно. У меня есть список nid, можно в массив его добавить., как вывести в определенный вид не понимаю. views_embed_view($name, $display_id="page_1"); если смотреть вот в этом примере... то как связать это со списком nid-ов.
Так понятнее?
Вообще то... не прояснилось:)
$nids = array(node_load(26320), node_load(26321));
$name_view = 'podbor';
$views = views_embed_view($name_view, 'page_1', $nids);
print $views;
$form['markup']['#markup'] = render($views);
Этот вариант ничего не выводит на экран...
$nids = array(26320, 26321); - так тоже пусто..
Только вот так выводит:
$node = node_load(26320);
$node1 = node_load(26321);
$view = node_view($node);
$view1 = node_view($node1);
$form['markup']['#markup'] = ''.render($view).''.render($view1);
Не надо в первом случае делать node_load(). Просто передайте им строку из nid материалов.
Супер статья, Спасибо!
Спасибо за статью. Как раз то, что искал!
Добрый день.
Отличная статья.
Есть вопрос: при выводе методом подобным указанному в статье (друпал 6) в браузерах Опера 11.60 и выше не закрывается окошко селекта
Похожий глюк наблюдается и в примере (ссылка http://pilipok.ru/ajax_page)
Как победить оперу?
Комментировать