Использование tmpl=component в AJAX запросах для Joomla! на примере AJAX пагинации в блоге материалов

Использование tmpl=component в AJAX запросах для Joomla! на примере AJAX пагинации в блоге материалов

Для любого современного сайта функционал с использованием AJAX запросов не является чем-то новым. К тому же использование AJAX запросов в Joomla! значительно упрощает большое количество различных инструментов, которым можно посвятить отдельный рассказ. В этой статье хотелось бы поделиться мыслями на счет одного не совсем стандартного способа использования параметра tmpl=component при выполнении AJAX запросов и обновлении контентного блока сайта. А также обратить внимание на подводные камни, которые возникают при использовании данного метода.

Постановка задачи

В первую очередь стоит сформулировать задачу, чтобы понимать в каких именно случаях можно воспользоваться описанным ниже методом. Предположим, что нам нужно полностью обновить контентную область сайта. Основной блок без шапки, подвала сайта и т.д. В шаблоне сайта на Joomla! этот блок выводится как .

Также мы знаем запрос, с помощью которого можно получить новый html. Нам не нужно самим формировать какие-либо методы для выборки данных из базы и их подготовки к отображению на сайте. Это особенно удобно, если мы не являемся разработчиком компонента, для которого хотим добавить AJAX функционал. Главное условие, чтобы запрос отправлялся AJAX-ом и полученный html обновлялся в контентном блоке сайта без перезагрузки страницы.

Пример с Блогом материалов в Joomla!

Теперь приведем пример конкретной задачи, которая подходит под описанные выше условия. Возьмем самый обычный менеджер материалов Joomla!, который тоже является ее отдельным компонентом, и попробуем сделать на странице блога категории из обычного блока с пагинацией (блок со ссылками на страницы 1, 2, 3, ...), AJAX пагинацию. Этот пример не требует сложной установки/настройки дополнительных элементов сайта.

Поскольку Менеджер материалов уже есть в базовом пакете поставки Joomla!, нам нужно только добавить несколько тестовых статей через для какой либо категории. Далее создать пункт меню с типом Блог категории (можно и Список материалов категории, но дальше в примерах рассматривается случай с блогом), выбрать категорию, в которую добавили тестовые материалы и установить ее параметры так, чтобы на этой странице появилась пагинация.

Например, добавить 5 тестовых материалов для тестовой категории Joomla!. При этом на вкладке Параметры макета Блога, при создании пункта меню, можно установить параметр Во всю ширину, например, в 3. Проверяем, что в результате на новой тестовой странице, на которую ведет созданный пункт меню, выводятся тестовые материалы и есть пагинация.

Блог материалов Joomla! с пагинацией

В результате мы хотим, чтобы при нажатии на ссылки в блоке пагинации отправлялся AJAX запрос и обновлялся контентный блок страницы со списком новых материалов и новым блоком пагинации. При этом мы знаем сам запрос, он присутствует в href ссылки в блоке пагинации. Компонент управления материалов в Joomla! умеет выводить как постраничную навигацию, так и список материалов на конкретной странице. Тут нам не нужно ничего придумывать или дописывать. Достаточно воспользоваться имеющейся ссылкой, которая и является нашим GET запросом. Этот запрос и будет отправляться при помощи AJAX. Например

[имя_сайта]/index.php?option=com_content&view=category&layout=blog&id=9&Itemid=136&limitstart=3

Пример с AJAX пагинацией в блоге материалов тут в большей мере для иллюстрации принципа и не является оптимальным или универсальным решением.

О параметре tmpl=component

Использование этого параметра упоминается в официальной документации для Joomla! в контексте создания для компонента функционала печати страниц в модальном окне. Этот функционал нам сейчас никак не поможет, но на счет модального окна стоит рассмотреть подробнее. Особенность в том, что при запросе к какому-либо компоненту с использованием параметра tmpl=component Joomla! возвращает html, который содержит тег <head> с подключением всех метатегов, css, js и т.д. А в теге <body> содержится весь контент страницы без хедера, футера и других дополнительных элементов шаблона.

Пример страницы материала с параметром tmpl=component

Такое поведение определяется наличием файла component.php в корневой директории текущего шаблона сайта. Официальная документация Joomla! говорит о том, что возможность создания такого файла в шаблоне появилась еще в Joomla! версии 1.5 и предназначена для формирования страниц удобных для печати материалов сайта.

templates/[имя_текущего_шаблона_Joomla!]/component.php

Например, для Joomla! 3.6.4 в шаблоне protostar, который поставляется вместе с CMS, этот файл имеет следующий вид

<?php
/**
 * @package     Joomla.Site
 * @subpackage  Templates.protostar
 *
 * @copyright   Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

$app             = JFactory::getApplication();
$doc             = JFactory::getDocument();
$this->language  = $doc->language;
$this->direction = $doc->direction;

// Output as HTML5
$doc->setHtml5(true);

// Add JavaScript Frameworks
JHtml::_('bootstrap.framework');

// Add Stylesheets
$doc->addStyleSheetVersion($this->baseurl . '/templates/' . $this->template . '/css/template.css');

// Load optional rtl Bootstrap css and Bootstrap bugfixes
JHtmlBootstrap::loadCss($includeMaincss = false, $this->direction);
?>
<!DOCTYPE html>
<html lang="<?php echo $this->language; ?>" dir="<?php echo $this->direction; ?>">
<head>
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
	<jdoc:include type="head" />
	<!--[if lt IE 9]><script src="<?php echo JUri::root(true); ?>/media/jui/js/html5.js"></script><![endif]-->
</head>
<body class="contentpane modal">
	<jdoc:include type="message" />
	<jdoc:include type="component" />
</body>
</html>

Все это отлично работает, если мы хотим вывести какую-то страницу в модальном окне, например, для печати. Но при AJAX запросе нам нужно будет обновлять контент текущей страницы сайта, а повторное подключение всех скриптов может сломать логику их поведения. Конечно, мы уже знаем, где лежит тот самый component.php и могли бы изменить его как угодно, но это в свою очередь может навредить страницам печати (или каким-то другим, мы не можем знать заранее как этот функционал используют разработчики других приложений). Исходя из этого, будем решать поставленную задачу не меняя данный файл шаблона.

Пишем код AJAX пагинации материалов

Приступим непосредственно к коду примера, описанного выше. Наша цель сделать из обычной пагинации в блоге материалов AJAX пагинацию.

Шаблон блога материалов Joomla!

В первую очередь нужно воспользоваться перекрытием вывода блога категории в текущем шаблоне Joomla!. Для этого достаточно скопировать php файлы каталога

components/com_content/views/category/tmpl

в папку

templates/[имя_текущего_шаблона_Joomla!]/html/com_content/category

Это перекроет вывод, как стандартного вида категории, так и блога. Если не хотите перекрывать стандартный вид категории, ограничьтесь при копировании php файлами, имя которых начинается на blog.

Если такой подход перекрытия вывода в шаблоне Joomla! вам еще не знаком, то подробнее о нем рассказывается в статье Перекрытие вывода компонентов и модулей в шаблоне Joomla!.

Теперь отображение страницы блога материалов из тестовой категории определяется в файле blog.php, что несложно проверить добавив в этот файл какой-то новый тег или css класс. После обновления страницы на сайте эти изменения должны быть видны.

Чтобы заменить контент текущей страницы новым html, который мы получим в результате работы AJAX запроса, обернем все содержимое файла blog.php в новый блок с id="blog_base_block". В этом же файле нужно подключить скрипт будущей AJAX пагинации, который будет оформлен как небольшой jQuery плагин, и css файл с дополнительными стилями (тут будут стили для блокировки страницы и отображения иконки загрузки на время выполнения AJAX запроса). В результате файл blog.php схематично будет иметь следующий вид

<?php

...

JHtml::script('com_content/jquery.ajax.pagination.js', false, true);
JHtml::stylesheet('com_content/ajax.pagination.css', array(), true);
?>
<div id="blog_base_block">
	
	...
	
</div>

Стоит обратить внимание на то, что файл jQuery плагина и css файл стилей загружаются из папки media. Пути к данным файлам будут следующими

media/com_content/js/jquery.ajax.pagination.js

media/com_content/css/ajax.pagination.css

Соответственно при подключении файлов указывается только путь к папке компонента и название файла com_content/jquery.ajax.pagination.js, а третий параметр в функции подключения $relative = true.

jQuery плагин для AJAX пагинации

Основной скрипт, который отвечает за отправку AJAX запроса и обновление контентного блока на странице сайта - jquery.ajax.pagination.js. Он оформлен в виде jQuery плагина. Сам jQuery уже подключается в Joomla!, либо его можно подключить самостоятельно в шаблоне следующим php кодом: JHtml::_('jquery.framework');

Тема написания плагинов для jQuery выходит за рамки данной статьи. Ссылка на скачивание полного кода всех файлов есть в конце статьи. Рассмотрим только части данного плагина, которые касаются особенностей работы с запросом Joomla! и методики обновления контента страницы.

initNextPageClickEvent: function() {
    var ajaxPagination = this;
    this.paginationBlock.find('a').on('click', function (event) {
        event.preventDefault();
        var href = $(this).attr('href');
        if (href == undefined) {
            return;
        }
        ajaxPagination.sendNextPageRequest(href);
    });
},

В этой функции на все ссылки блока пагинации навешивается обработчик события нажатия на данную ссылку. В первую очередь отменяется стандартное действие при нажатии на нее - это переход по URL указанном в href параметре. Далее скрипт извлекает href параметр ссылки и передает управление функции sendNextPageRequest.

sendNextPageRequest: function(nextPageUrl) {
    if (nextPageUrl.indexOf('&tmpl=component') < 0) {
        nextPageUrl += '&tmpl=component';
    }
    this.showLoading();
    var ajaxPagination = this;
    $.ajax({
        url: nextPageUrl,
        type: 'GET',
        dataType: 'html',
        success: function (result) {
            var newContent = $('<div/>').append(result).find(ajaxPagination.options.contentBaseBlockSelector);
            $(ajaxPagination.options.contentBaseBlockSelector).replaceWith(newContent);
        },
        error: function (error) {
            console.log('Error sending ajax request\nStatus code: ' + error.status + '\nStatus text: ' + error.statusText);
        },
        complete: function () {
            ajaxPagination.hideLoading();
        }
    });
},

В этой функции в первую очередь проверяется наличие параметра tmpl=component в полученной ссылке и, если такого параметра нет, то он добавляется к ней. Таким образом при запросе мы получим интересующий нас контент страницы без лишнего html кода. Далее вызывается функция отображения иконки при выполнении AJAX запроса. Сам AJAX запрос выполняется стандартными методами, присутствующими в библиотеке jQuery.

AJAX запрос проинициализирован следующим образом

url: nextPageUrl - определяет URL запроса, в котором уже добавлен параметр tmpl=component. При этом все параметры для перехода на конкретную страницу (страница 1, 2, 3 и т.д.) Joomla! добавила самостоятельно при формировании страницы блога материала с пагинацией.

type: 'GET' - параметр, определяющий тип запроса, в данном случае GET

dataType: 'html' - тип получаемых в результате запроса данных

Функция success

На этой функции стоит остановиться отдельно. Она вызывается при успешном выполнении данного AJAX запроса и получении html в результате его работы. Этот html код записывается в параметр result и именно его мы хотим вставить вместо имеющегося содержимого страницы. Но как мы уже упоминали выше, если вставить этот html как есть, то на странице продублируются все метатеги, подключения js файлов и все остальное, что находится в теге <head> текущего шаблона Joomla!. Чтобы избежать данной ситуации нам нужно выделить в данном коде только основной блок, который изначально был нами обернут в тег <div id="blog_base_block">.

Но здесь есть еще один подводный камень. Если мы просто преобразуем результирующий html в jQuery объект, то получим целый массив объектов, в котором будет сложно выполнить поиск нужного нам тега.

console.log($(result));

покажет что-то похожее на следующий скрин

Результат AJAX запроса в виде объекта jQuery

Чтобы удобно было выполнить поиск нужного тега методами jQuery мы можем создать обертку и поместить результирующий html в нее.

console.log($('<div/>').append(result));

Результат AJAX запроса в виде объекта jQuery c оберткой

И уже в получившемся объекте используя метод find находим нужный нам тег с контентом, помещая его при этом в отдельную переменную. Осталось только заменить содержимое этого же блока на текущей страницы, что просто делается с использованием jQuery метода replaceWith.

Функция error

Эта функция вызывается при возникновении ошибки во время выполнения AJAX запроса. В ней мы только выводим в консоль сообщение об ошибке.

Функция complete

Данная функция вызывается при завершении AJAX запроса вне зависимости от того, возникли ли ошибки в процессе его выполнения. В ней мы только убираем иконку загрузки, которая отображалась во время работы AJAX запроса.

Инициализация плагина в шаблоне блога

После завершения работы над файлом jQuery плагина его нужно проинициализировать в файле blog.php. Там же нужно добавить тег, который будет отвечать за отображение иконки выполнения AJAX запроса.

Кроме этого блок пагинации также обернут тегом с id="category_blog_pagination_block", который используется при инициализации плагина AJAX пагинации.

<?php

...

JHtml::script('com_content/jquery.ajax.pagination.js', false, true);
JHtml::stylesheet('com_content/ajax.pagination.css', array(), true);
?>
<div id="blog_base_block">
    <script type="text/javascript">
        jQuery(function ($) {
            $('#category_blog_pagination_block').initAjaxPagination({});
        });
    </script>
    <div class="blog_ajax_loading"></div>

    ...

    <?php if (($this->params->def('show_pagination', 1) == 1 || ($this->params->get('show_pagination') == 2)) && ($this->pagination->get('pages.total') > 1)) : ?>
        <div id="category_blog_pagination_block" class="pagination">
            <?php echo $this->pagination->getPagesLinks(); ?>
        </div>
    <?php endif; ?>
</div>

После этого можно проверять работу пагинации в блоге материалов.

загрузка следующей страницы блога с помощью AJAX запроса

Выводы

В результате мы узнали, что для любого запроса в Joomla! можно добавить параметр tmpl=component, который позволяет получить только ту часть страницы, которую генерирует компонент, без лишних элементов шаблона. Также разобрались как можно адаптировать полученный html для обновления на текущей странице сайта, используя простые jQuery методы. А это значит, что в отдельных случаях не обязательно самостоятельно писать собственные контроллеры для получения данных и специально адаптировать их под конкретный шаблон, а можно воспользоваться уже имеющейся и универсальной возможностью движка Joomla!.

Еще раз обратим внимание на то, что пример с AJAX пагинацией блога материалов выступает в качестве иллюстрации работы описанного подхода. Реализовать AJAX пагинацию для компонента материалов Joomla! или для другого компонента можно используя разные подходы, а данный способ не претендует на звание оптимального.

Схожий принцип мы также использовали и в собственных проектах. Например, в модуле фильтрации товаров для JoomShopping, который также является иллюстрацией работы описанного подхода. Посмотреть на его работу можно на нашем демо-сайте.

Скачать архив со всеми файлами AJAX пагинации для блога материалов можно по ссылке. Для его установки достаточно просто скопировать файлы архива в корень папки с установленной Joomla!.

Внимание! Не используйте этот вариант AJAX пагинации на рабочих сайтах без предварительного тестирования и адаптации. Сделайте резервную копию сайта перед копирование файлов приложения.

Тестирование проводилось на Joomla! версии 3.6.4 и стандартном шаблоне - protostar, но при этом перекрыть файлы блога материалов можно в любом установленном на Joomla! шаблоне.

Метки: Joomla!, AJAX