Диалоги Ajax в Drupal - это хороший способ представить контент пользователю без необходимости покидать страницу.
Это полезно при представлении списка элементов или ссылки на что-то вроде страницы условий. Пользователи могут нажать на ссылку и просмотреть содержимое в виде диалогового окна.
Создание диалогов в Drupal встроено в платформу и осуществляется через компонент диалога jQuery UI.
Drupal предоставляет способ создания следующих типов диалогов.
- Обычные диалоги - диалоговое окно отображается поверх содержимого страницы. На странице можно одновременно показывать несколько диалогов.
- Модальные диалоги - диалоговое окно создает наложение, которое отключает другие элементы на странице. На странице одновременно может отображаться только одно модальное диалоговое окно.
- Диалоги "От холста" - диалоговое окно скользит из края окна браузера. Это полезно, если вы хотите показать много контента, а также является лучшим вариантом для отображения диалоговых окон в мобильных представлениях.
Существует несколько способов создания диалогов, но документации по этому вопросу очень мало.
В этой статье будет рассмотрено, как создавать диалоги разных типов в Drupal и несколькими различными способами, в том числе о том, как редакторы контента могут легко добавлять диалоги в контент с небольшими знаниями программирования.
Создание диалогов с использованием классов PHP
Диалоги Ajax генерируются в Drupal с использованием ряда классов PHP. Различные классы используются для создания различных типов диалогов. Во-первых, давайте рассмотрим создание обычного диалогового окна Ajax.
Создание обычных диалогов Ajax с помощью OpenDialogCommand
Drupal использует класс Drupal\Core\Ajax\OpenDialogCommand для создания диалогов, которые, в свою очередь, используют компонент jQuery UI для отображения диалога пользователю. Хотя существуют разные способы создания диалогов, все они используют или расширяют этот класс, поэтому это хорошая отправная точка для работы с диалогами Drupal.
Как предполагает пространство имен классов, OpenDialogCommand является компонентом командных систем Ajax в Drupal, и поэтому должен быть возвращен как часть объекта AjaxResponse. Действие контроллера можно использовать для создания AjaxResponse и добавления команды для открытия диалогового окна. Для этого нам также понадобится действие вторичного контроллера, чтобы инициировать обратный вызов по подключенному каналу Ajax.
Чтобы использовать класс OpenDialogCommand, нам нужно создать маршрут, который указывает на действие контроллера. Это будет добавлено в файл под названием mymodule.routing.yml.
mymodule_display_dialog:
path: '/test-dialog'
defaults:
_controller: '\Drupal\mymodule\Controller\DialogController::createModal'
requirements:
_access: 'TRUE'
Действие контроллера вернет объект AjaxResponse, содержащий объект OpenDialogCommand. Чтобы сделать возвращаемое содержимое более релевантным, мы загружаем и визуализируем узел в теле командного диалога.
Также обратите внимание, что частью возвращаемого объекта AjaxResponse является библиотека core/drupal.dialog.ajax. Это необходимо для загрузки диалогового компонента jQuery UI, который генерирует диалоги, и код Drupal JavaScript, который преобразует возвращенный ответ Ajax во что-то, что может понять компонент диалога. Мы можем добавить эту библиотеку до или после этого момента, если она доступна при визуализации диалогового ответа.
Вот класс контроллера, который вернет ответ Ajax и отобразит диалоговое окно.
<?php
namespace Drupal\mymodule\Controller;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\OpenDialogCommand;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
class DialogController extends ControllerBase {
protected $entityTypeManager
public function __construct($entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
);
}
public function createDialog() {
$node = $this->entityTypeManager->getStorage('node')->load(1);
$viewBuilder = $this->entityTypeManager->getViewBuilder('node');
$content = $viewBuilder->view($node, 'teaser');
$title = $node->getTitle();
$response = new AjaxResponse();
$attachments['library'][] = 'core/drupal.dialog.ajax';
$response->setAttachments($attachments);
$response->addCommand(new OpenDialogCommand('#my-dialog-selector', $title, $content, ['width' => '70%']));
return $response;
}
}
Обратите внимание, что в приведенном выше примере используется внедрение зависимостей для извлечения необходимых служб.
Объект OpenDialogCommand имеет ряд параметров в своем конструкторе, поэтому давайте разберем их здесь.
- $selector - это идентификатор обертки вокруг генерируемого диалога, который устанавливается в "my-dialog-selector". Используйте идентификатор, если вы хотите нацелить диалоговое окно с помощью стилей или взаимодействовать с ним с помощью JavaScript.
- $title - это заголовок диалогового окна, который в приведенном выше коде мы устанавливаем на заголовок узла.
- $content - содержимое диалогового окна, которое может быть в виде строки или массива рендеринга. В приведенном выше коде мы добавляем тизер узла в виде массива рендеринга к содержимому.
- $dialog_options - это необязательный массив параметров, которые могут быть переданы в диалоговое окно jQuery UI. Вы можете увидеть полный список опций в документации по диалоговому интерфейсу jQuery. В приведенном выше примере мы устанавливаем ширину диалогового окна на 70%.
- $settings - это необязательный массив параметров, который будет передан в поведение Drupal в содержимом диалогового окна.
Это действие контроллера ничего не делает само по себе, поэтому давайте создадим ссылку с включенным Ajax, чтобы мы могли его запустить. Все, что нам нужно сделать, это создать маршрут, который мы можем использовать для отображения массива рендеринга, содержащего ссылку, которая откроет диалоговое окно.
mymodule_display_page:
path: '/test-page'
defaults:
_controller: '\Drupal\mymodule\Controller\DialogController::displayPage'
requirements:
_access: 'TRUE'
Само действие содержит массив рендеринга, который добавляется в тот же класс DialogController, который мы создали ранее. Все, что мы делаем в этом действии, это создаем массив рендеринга, который выведет ссылку, указываемую на действие диалогового контроллера, которое мы создали на предыдущем шаге.
Однако, чтобы включить Ajax, нам нужно добавить класс use-ajax к атрибутам ссылки. Это позволяет Drupal знать, что это ссылка, которая должна возвращать ответ Ajax, а не навигационную ссылку.
Вот новое действие, которое отобразит ссылку на пользователя.
public function displayPage() {
$output = [];
$output['a_dialog_link'] = [
'#type' => 'link',
'#url' => new Url('mymodule_display_modal'),
'#title' => 'node in dialog',
'#attributes' => [
'class' => ['use-ajax'],
],
];
$output['#attached']['library'][] = 'core/drupal.dialog.ajax';
return $output;
}
Обратите внимание на включение библиотеки core/drupal.dialog.ajax на этой стороне ответа Ajax. Это касается пограничного случая, когда анонимные пользователи не получают все необходимые библиотеки из ответа Ajax и вместо этого нуждаются в том, чтобы эти библиотеки были загружены заранее. Если у вас возникли проблемы с тем, что анонимные пользователи не могут использовать диалоги, прикрепите библиотеку диалогов к исходному ответу страницы, а не к ответу.
Если мы сейчас посетим страницу по адресу "/test-page", мы увидим ссылку с текстом "узел в диалоговом окне". Когда мы нажимаем на эту ссылку, мы должны видеть содержимое узла, отображаемое в виде диалогового окна, как и скриншот в начале этого раздела.
Создание модальных диалогов Ajax с помощью OpenModalDialogCommand
Модальное диалоговое окно Ajax такое же, как и обычное диалоговое окно Ajax, за исключением того, что оно предотвращает взаимодействие с остальной частью страницы. Это делается путем создания наложения под диалогом, но над другими элементами, что предотвращает нажатие ссылок за диалогом.
Есть два способа, с помощью которых мы можем создать модальное диалоговое окно Ajax.
Во-первых, просто добавьте опцию "модальный" в массив опций, который мы отправляем в OpenDialogCommand. Это сообщит компоненту диалога jQuery UI о том, что мы хотим создать модальное диалоговое окно.
$options = [
'width' => '70%',
'modal' => TRUE,
];
$response->addCommand(new OpenDialogCommand('#my-dialog-selector', $title, $content, $options));
Второй вариант - использовать специальный класс под названием Drupal\Core\Ajax\OpenModalDialogCommand. Этот класс расширяет класс OpenDialogCommand и жестко кодирует один и тот же модальный вариант, чтобы быть верным. Он также удаляет селектор из конструктора и жестко кодирует его как "#drupal-modal".
Чтобы использовать класс OpenModalDialogCommand в действии createDialog, нам просто нужно добавить класс в раздел пространств имен в верхней части файла.
use Drupal\Core\Ajax\OpenModalDialogCommand;
Затем мы просто меняем команду createDialog, чтобы использовать команду OpenModalDialogCommand вместо команды OpenDialogCommand.
public function createDialog() {
$node = $this->entityTypeManager->getStorage('node')->load(1);
$viewBuilder = $this->entityTypeManager->getViewBuilder('node');
$content = $viewBuilder->view($node, 'teaser');
$title = $node->getTitle();
$response = new AjaxResponse();
$attachments['library'][] = 'core/drupal.dialog.ajax';
$response->setAttachments($attachments);
$response->addCommand(new OpenModalDialogCommand($title, $content, ['width' => '70%']));
return $response;
}
Теперь, когда мы нажмем на ссылку диалога, мы увидим модальное окно вместо обычного диалогового окна.
Создание диалогов Canvas Ajax с помощью OpenOffCanvasDialogCommand
Диалоги Ajax с холста либо скользят с правой стороны, либо с верхней части страницы. Это отличается от модальных и обычных диалогов Ajax, которые появляются в середине экрана.
Чтобы использовать этот тип диалогового окна, вам необходимо включить класс Drupal\Core\Ajax\OpenOffCanvasDialogCommand в область пространства имен в верхней части файла.
use Drupal\Core\Ajax\OpenOffCanvasDialogCommand;
Класс OpenOffCanvasDialogCommand расширяет класс OpenDialogCommand и по сути является обычным диалогом с несколькими установленными параметрами. Он используется более или менее так же, как и класс OpenModalDialogCommand, так как он принимает меньше опций, чем класс OpenDialogCommand.
public function createDialog() {
$node = $this->entityTypeManager->getStorage('node')->load(1);
$viewBuilder = $this->entityTypeManager->getViewBuilder('node');
$content = $viewBuilder->view($node, 'teaser');
$title = $node->getTitle();
$response = new AjaxResponse();
$attachments['library'][] = 'core/drupal.dialog.ajax';
$attachments['library'][] = 'core/drupal.dialog.off_canvas';
$response->setAttachments($attachments);
$response->addCommand(new OpenOffCanvasDialogCommand($title, $content, ['width' => '70%']));
return $response;
}
Ключевая часть этого заключается в том, что вам также нужно добавить библиотеку core/drupal.dialog.off_canvas на страницу, чтобы диалоговое окно off canvas работало правильно. Если вы загрузите диалоговое окно без этой библиотеки, вы получите только обычное диалоговое окно Ajax. Вы можете добавить его в ответ Ajax как часть команды off canvas dialog, как и другие диалоговые окна Ajax.
Самым надежным способом сделать это является добавление его в массив рендеринга, который генерирует связь в качестве вложения к массиву рендеринга.
public function displayPage() {
$output = [];
$output['a_dialog_link'] = [
'#type' => 'link',
'#url' => new Url('mymodule_display_modal'),
'#title' => 'node in dialog',
'#attributes' => [
'class' => ['use-ajax'],
],
];
$output['#attached']['library'][] = 'core/drupal.dialog.ajax';
$output['#attached']['library'][] = 'core/drupal.dialog.off_canvas';
return $output;
}
По умолчанию диалоговое окно off canvas ajax появится с правой стороны. Если вы хотите изменить это в верхней части страницы, вы можете установить свойство $position на top. Это последнее свойство в конструкторе после свойства $settings, поэтому его необходимо установить в NULL. Поскольку настройка ширины не имеет смысла в этом контексте.
$response->addCommand(new OpenOffCanvasDialogCommand($title, $content, [], NULL, 'top'));
С этим изменением диалоговое окно появится в верхней части экрана.
Добавление диалогового окна Ajax с использованием атрибутов
Хорошая новость заключается в том, что вам не нужно проходить через все это, чтобы создать диалоговое окно Ajax, вы можете просто указать Drupal, что вы хотите создать диалоговое окно Ajax, и система заполнит пробелы для вас. Есть несколько способов сделать это: использование массива рендеринга для добавления атрибутов, добавление атрибутов в определения ссылок и редактирование HTML.
Все эти методы используют одни и те же атрибуты в HTML, чтобы сообщить Drupal, что для этой ссылки будет использоваться диалоговое окно Ajax. Класс 'use-ajax' все еще необходим, чтобы сообщить Drupal, что это ссылка Ajax, но мы также добавляем пару дополнительных атрибутов для настройки диалогового окна.
- data-dialog-type - это тип создаваемого диалога, который может быть одним из значений dialog, dialog.off_canvas,dialog.off_canvas_top и modal.
- data-dialog-options - это набор значений в кодировке json, которые вы можете передать компоненту диалога. По сути, это то же самое, что и параметр options из объектов команды диалогового окна и позволяет установить любое значение в диалоговом окне.
- data-dialog-renderer - Этот параметр может добавить дополнительную информацию о том, как визуализировать диалоговое окно. Он используется не часто, но может быть использован для преобразования обычного диалога в диалоговое окно off canvas, используя либо параметры off_canvas, либо off_canvas_top. В более технических терминах он добавляется к параметру _wrapper_format запроса Ajax и может использоваться для обновления способа обработки запроса.
Давайте рассмотрим несколько различных способов, которыми мы можем использовать это.
Добавление диалогового окна Ajax с помощью массива визуализации
Чтобы создать диалоговое окно Ajax с использованием массива рендеринга, вам необходимо добавить атрибуты диалогового окна Ajax.
В следующем примере будет сгенерироваться ссылка на страницу контента и использоваться модальное диалоговое окно шириной 70%, чтобы показать этот контент пользователю. Мы используем класс Drupal\Component\Serialization\Json для обработки кодировки json здесь.
public function displayPage() {
$output = [];
$node = $this->entityTypeManager->getStorage('node')->load(1);
$url = $node->toUrl()->setAbsolute();
$output['a_dialog_link'] = [
'#type' => 'link',
'#url' => $url,
'#title' => 'node in dialog',
'#attributes' => [
'class' => ['use-ajax'],
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([
'width' => '70%',
]),
],
];
$output['#attached']['library'][] = 'core/drupal.dialog.ajax';
return $output;
}
Одна из проблем, которая может вызвать у вас проблемы с этими методами атрибутов, заключается в том, как отображается конечный результат, поэтому давайте рассмотрим это здесь, прежде чем двигаться дальше.
Когда вы открываете диалоговое окно Ajax, вы можете заметить что-то немного странное, если у вас включены комментарии к узлу, на который вы ссылаетесь.
Это вызвано тем, что рендеринг по умолчанию для узлов является "полным", и поэтому форма комментария отображается в ответе на запрос Ajax. Drupal автоматически обнаруживет любые кнопки в ответе и отобразит их в нижнем колонтитуле диалогового окна. Это поведение можно отключить с помощью опции drupalAutoButtons при создании объектов команд диалогового окна, но при передаче такого массива рендеринга ссылок у нас нет доступа к этому параметру.
Чтобы обойти это, нам нужно изменить режим рендеринга узла, когда к нему осуществляется доступ через диалоговое окно Ajax. Затем мы можем выбрать режим рендеринга, в котором не распечатана форма комментариев, а затем создать менее запутанное диалоговое окно.
Это можно сделать с помощью крючка hook_entity_view_mode_alter(), который мы используем для замены режима просмотра в зависимости от контекста ответа. Ниже показана реализация этого хука, который прослушивает статьи, отображаемые в полном режиме через модальное диалоговое окно Ajax, и меняет его на тизер.
function mymodule_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
if ($entity->getEntityTypeId() == 'node' && $entity->bundle() == 'article' && $view_mode == 'full') {
$isAjax = \Drupal::request()->isXmlHttpRequest();
$wrapperFormat = \Drupal::request()->get('_wrapper_format');
if ($isAjax === TRUE && $wrapperFormat == 'drupal_modal') {
$view_mode = 'teaser';
}
}
}
С этим крючком наш узел статьи распечатывается без кнопок комментариев. Диалоговое окно Ajax по сути функционирует так же, как и при создании команд Ajax в начале этой статьи. Вероятно, рекомендуется иметь режим рендеринга для диалоговых окон перед настройками, так как он поддерживает ваши диалоги в согласованном состоянии.
В качестве побочного примечания осторожно при использовании параметра _wrapper_format в запросе Ajax. Это можно изменить с помощью атрибута data-dialog-renderer, который добавит информацию в конец формата обертки. Например, если вы установите атрибут data-dialog-type как "dialog", а атрибут data-dialog-renderer - "off_canvas", то строка _wrapper_format будет "drupal_dialog.off_canvas".
Параметры drupalAutoButtons установлены на false в OpenOffCanvasDialogCommand, и поэтому кнопки не будут отображаться. Тем не менее, по-прежнему рекомендуется адаптировать контент в соответствии с дисплеем и выбрать правильный режим отображения при отображении в диалоговом окне Ajax.
Для полноты давайте рассмотрим два способа, которыми вы можете показать пользователям диалоговое окно off canvas.
Создать диалоговое окно off canvas, используя только атрибут data-dialog-type.
public function displayPage() {
$output = [];
$node = $this->entityTypeManager->getStorage('node')->load(1);
$url = $node->toUrl()->setAbsolute();
$output['a_dialog_link'] = [
'#type' => 'link',
'#url' => $url,
'#title' => 'node in dialog',
'#attributes' => [
'class' => ['use-ajax'],
'data-dialog-type' => 'dialog.off_canvas',
],
];
$output['#attached']['library'][] = 'core/drupal.dialog.ajax';
$output['#attached']['library'][] = 'core/drupal.dialog.off_canvas';
return $output;
}
Чтобы создать диалоговое окно off canvas с использованием комбинации атрибутов data-dialog-type и data-dialog-renderer.
public function displayPage() {
$output = [];
$node = $this->entityTypeManager->getStorage('node')->load(1);
$url = $node->toUrl()->setAbsolute();
$output['a_dialog_link'] = [
'#type' => 'link',
'#url' => $url,
'#title' => 'node in dialog',
'#attributes' => [
'class' => ['use-ajax'],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'off_canvas',
],
];
$output['#attached']['library'][] = 'core/drupal.dialog.ajax';
$output['#attached']['library'][] = 'core/drupal.dialog.off_canvas';
return $output;
}
Если вас интересует, как атрибуты изменяются в диалоговых окнах, посмотрите классы Drupal\Core\Render\MainContent\DialogRenderer, Drupal\Core\Render\MainContent\ModalRenderer и Drupal\Core\Render\MainContent\ Эти классы используются для преобразования запроса Ajax в AjaxResponse, который содержит разновидность класса команд диалога.
Добавление диалогового окна Ajax с определениями ссылок
Можно добавить параметры диалога Ajax к ссылкам, определенным в модулях с использованием информации об атрибутах.
Следующий код будет создан в файле mymodule.links.menu.yml и отображает страницу содержимого с помощью диалогового окна off canvas ajax. Я должен отметить, что, вероятно, не самая лучшая идея добавлять жестко закодированные ссылки на такой контент, но он показывает точно такое же диалоговое окно, как и другие примеры в этой статье.
mymodule.example_node:
title: 'node in dialog'
route_name: entity.node.canonical
route_parameters: {node: '1'}
menu_name: main
options:
attributes:
class: ['use-ajax']
data-dialog-type: dialog
data-dialog-renderer: off_canvas
Вам все еще нужно убедиться, что библиотеки core/drupal.dialog.ajax или core/drupa.dialog.off_canvas доступны по ссылке, чтобы это работало. Это можно сделать с помощью хука hook_page_attachments_alter() или просто добавить библиотеку в слой темы, чтобы она всегда была доступна.
Добавление диалогового окна Ajax в содержимое
Наконец, ваши редакторы также могут добавлять диалоги Ajax в контент, просто добавляя правильные атрибуты к ссылке. По сути, это то, что мы делали, добавляя атрибуты для рендеринга массивов и ссылок, и будет работать до тех пор, пока ваши пользователи имеют доступ к добавлению этих атрибутов.
Например, мы могли бы изменить обычную ссылку на страницу содержимого в модальное диалоговое окно Ajax, добавив класс use-ajax и добавив тип data-dialog в качестве модального к ссылке.
<a class="use-ajax" data-dialog-type="modal" href="/node/1">node in dialog</a>
Также можно передавать параметры с помощью атрибута data-dialog-options. Таким образом, ширина модального окна составляет 70%.
<a class="use-ajax" data-dialog-options="{"width":"70%"}" data-dialog-type="modal" href="/node/1">node in dialog</a>
Вы должны быть осторожны с кодированием параметров в ваших параметрах здесь. Убедитесь, что вы изменили все кавычки на '"', чтобы они не мешали остальной части HTML на странице.
Заключение
Диалоговая система Drupal Ajax довольно мощная, но требует немного понимания. Сначала может показаться, что Drupal волшебным образом знает, как создавать эти диалоги, но это просто разные компоненты, правильно подключенные друг к другу. Существует несколько различных способов создания диалоговых окон, что усугубляет путаницу, но все они проходят через одни и те же несколько классов за кулисами.
Что замечательно в этой системе, так это то, что ваши пользователи также могут использовать ее, не зная много деталей программирования. Для изменения обычной ссылки на модальное всплывающее окно с поддержкой Ajax требуется всего два атрибута, и до тех пор, пока ваша тема включает в себя диалоговые библиотеки, она будет работать довольно хорошо.