Drupal → AJAX ссылки в Drupal 7 на примере листалки последних комментариев

16.04.2011

В Drupal 7 появился новый AJAX Framework, который позволяет абстрагироваться от написания Javascript кода.

Ниже приведён пример модуля, создающий блок с последними комментариями и ссылкой "more", клик на которой подгружает следующую пачку комментариев.

1. Создаём функцию, которая возвращает содержимое блока (т.е. последние комментарии и ajax ссылку "more"):

/**
 * Возвращает html код содержимого блока.
 */
function _last_comments_block_content($from_cid = NULL) {
  // Выбираем комментарии из базы
  $query = db_select('comment', 'c')
    ->fields('c', array('cid', 'nid', 'subject'))
    ->condition('status', COMMENT_PUBLISHED)
    ->orderBy('cid', 'DESC')
    ->range(0, 10);
  
  if (!is_null($from_cid)) {
    $query->condition('cid', $from_cid, '<');
  }
  
  $items = array();
  foreach ($query->execute() as $comment) {
    $items[] = l($comment->subject, 'node/' . $comment->nid, array('fragment' => 'comment-' . $comment->cid));
    $last_cid = $comment->cid;
  }
  
  $content = theme('item_list', array('items' => $items));
  $content .= '<div class="more-link">' . l('more', 'last-comments/' . $last_cid . '/nojs', array(
    'attributes' => array(
      'class' => array('use-ajax'),
      'title' => 'More comments',
    )
  )) . '</div>';
  
  return $content;
}

Чтобы наделить ссылку ajax функциями, ей нужно присвоить класс use-ajax и добавить к адресу аргумент nojs, чтобы в callback функции можно было опознать пользователей с отключённым javascript.

2. Создаём блок:

/**
 * Implements hook_block_info().
 */
function last_comments_block_info() {
  $blocks['last-comments'] = array('info' => 'Last comments');
  return $blocks;
}
 
/**
 * Implements hook_block_view().
 */
function last_comments_block_view($delta = '') {
  $block = array();
 
  if ($delta == 'last-comments') {
    $block['subject'] = 'Last comments';
    $block['content'] = '<div id="last-comments">' . _last_comments_block_content() . '</div>';
  
    // подключаем необходимые ajax библиотеки
    drupal_add_library('system', 'drupal.ajax');
    drupal_add_library('system', 'jquery.form');
  }
 
  return $block;
}

Для работы AJAX-фреймворка, нужно не забыть подключить библиотеки drupal.ajax (файл misc/ajax.js) и jquery.form (файл misc/form.js).

3. Регистрируем адрес, по которому будут возвращаться данные:

/**
 * Implements hook_menu().
 */
function last_comments_menu() {
  $items['last-comments/%'] = array(
    'title' => 'Last comments',
    'page callback' => 'last_comments_ajax_callback',
    'page arguments' => array(1),
    'delivery callback' => 'ajax_deliver', // данные, возвращённые ф-ей last_comments_ajax_callback(), будут отданы в json формате
    'access arguments' => array('access content'),
  );
  return $items;
}

В первом аргументе будет содержаться id последнего показанного комментария.

4. Реализуем callback указанный в hook_menu:

/**
 * Menu callback.
 */
function last_comments_ajax_callback($from_cid, $mode = NULL) {
  // Если у посетителя отключён javascript, то показываем ему сообщение
  if ($mode != 'ajax') {
    drupal_set_message('Enable Javascript');
    drupal_goto(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '<front>');
  }
  
  // обновляем содержимое блока
  $commands[] = ajax_command_html('#last-comments', _last_comments_block_content($from_cid));
  
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

В калбаке возвращается не html код блока, а набор команд для AJAX-фреймворка. Например с помощью команды:

ajax_command_html('some-selector', 'some-text')

мы как бы говорим браузеру — заменить текст в селекторе some-selector на some-text, а браузер на это выполняет js код:

$('some-selector').html('some-text');

Встроенных команд не так много, но выручает универсальная ajax_command_invoke, с помощью которой можно дёргать любые методы jQuery. Например, чтобы сменить адрес у ссылки (атрибут href) нужно добавить:

$commands[] = ajax_command_invoke('some-selector', 'attr', array('href', '/my/new/path'));

Что равносильно js коду:

$('some-selector').attr('href', '/my/new/path');

Ну вот и всё. Мы написали модуль без единой строчки яваскрипта. Исходники прилагаются.

Подробнее про Javascript, jQuery и AJAX разжевано в видео от лулаботов — jQuery and JavaScript in Drupal (конкретно про AJAX рассказывается в главе 10, по времени это 1:56).

Похожая статья на английском — Make a link use ajax in Drupal 7 (it's easy).
Видео урок на английском — Создание AJAX ссылок в Drupal 7.

Написанное актуально для
Drupal 7
Похожие записи

Комментарии

@andypost отлично. надеюсь когда-нибудь увижу в drupal ленивую загрузку целых модулей

Дмитрий
24.11.2013, 21:30

Такой вопрос.
Сделал ajax при клике на ссылку. После ajax отрабатывает:

ajax_command_invoke('some-selector', 'attr', array('href', '/my/new/path'));

на текущую ссылку.
Но после обновления href ссылки ajax продолжает работать по старому href.

Как сделать ребилд ajax элемента не используя js кода? Вообще есть такая возможность?
Т.к. я вообще нихрена не нагуглил... :(

попробуйте удалить у ссылки класс ajax-processed ну и с Drupal.attachBehaviors поиграться

Дмитрий
24.11.2013, 22:01

Спасибо, а как Drupal.attachBehaviors вызвать не из js, а как-нибудь используя команды?

Артем
17.04.2015, 11:01

Кто подскажет можно ли (если ДА то как?) использовать в commands селектор текущего контекста? Например в Jquery селектор "THIS".

Что-то вроде этого:
$commands[] = ajax_command_remove(this);

никак нельзя, это основная проблема ajax-фреймворка

Максим
12.07.2016, 11:30

Привет.

Спасибо за статью, очень помогла. Но возник вопрос:
есть форма, которая выводится функцией:

function form_fface($form, $form_state){
	$form['submitted'][$id]['lastname']		= f_text('Фамилия', 'before', 20);
	$form['submitted'][$id]['firstname']	= f_text('Имя', 'before', 18);
	$form['submitted'][$id]['middlename']	= f_text('Отчество', 'before', 22);
	return $form['submitted'][$id];
}

Вызов происходит

$form['submit'] = array(
		'#type' => 'submit',
		'#value' => t('Load content'),
		'#ajax' => array(
				'callback' => 'form_fface',
				'wrapper' => 'form-ajax-node-content0',
				'method' => 'replace',
				'effect' => 'fade',
			),
	);

Но остался вопрос, как задать темизацию этой формы. То есть выводит эти поля в столбец. А мне надо вывести их в строчку.

P.S. На самом деле конечно полей больше...

Какое отношение вопрос имеет к ajax ссылкам?

Максим
12.07.2016, 11:46

ну форма появляется после ajax ссылке.
То есть ajax должен возвращать форму обработанную как-то.
И где-то. А вот где и как, пока не могу найти :(

Сорри если не там задал вопрос.

Прочитайте статью внимательно - в menu callback для ajax ссылок надо возвращать команды, плюс формы в друпале вызываются с помощью drupal_get_form().

Можно ли в данном случае отключить троббер как в формах:

'#ajax' => array(
    'callback' => 'form_callback',
    'progress' => '',
),

Или только через стили?

Max, можно

Выключить тробер полностью:

'#ajax' => array(
    'callback' => 'form_callback',
    'progress' => array(
      'type' => 'none',
    ),
),

Убрать только тект тробера:

'#ajax' => array(
    'callback' => 'form_callback',
    'progress' => array(
      'message' => '',
    ),
),
Гость
12.04.2018, 08:41

У меня из за чего то вставка идёт всей страницы, а не блока, который запрашиваю и вставляю в ajax_command_html.
Какая должна быть кнопка, по которой происходит клик на страницы, которая будет вызывать адресс, по которому будет происходить возврат данных?

Гость
12.04.2018, 13:05

Ладно, вроде разобрался, что это ссылка, при том должна создаваться через l()
но почему если эту же ссылку вставлю через js, с соблюдением того же пути и классов, то она не будет работать?
(Это для того, что она не должна быть всегда, а только при определённых условиях и заменять обычную кнопку.)

Наверно потому, что не вызываете Drupal.attachBehaviors

Добавить комментарий