Работу с AJAX в OctoberCMS я уже раньше немного затрагивал, но для бэк-энда. А теперь можно затронуть и фронт. В этой заметке мы сделаем кнопочку Load More, чтобы подгружала без перезагрузки страницы новые посты блога (из плагина RainLab.Blog).
На первый взгляд может показаться, что для этого нужно влезть в компонент Posts этого плагина, но на самом деле — вовсе не нужно. Дело в том, что раньше из документации можно было подумать, что обработчики AJAX-событий нужно описывать только в классе контроллера (если в бэк-энде), или компонента (если во фронте). И поэтому не очень было понятно, как быть, когда ты хочешь получить красивый ajax-пейджер для блога. То ли лезть в код чужого плагина, что влечет проблемы с обновлениями. То ли писать свой собственный компонент, из которого грузить компонент Posts программно. Что ведет к сомнениям в том, надо ли вообще такой негибкой CMS пользоваться.
Но то ли документацию дополнили, то ли в глазах у меня прояснилось (а может и то, и другое) — в общем, выяснилось, что в хендлеры ajax-событий годятся не только методы контроллеров/компонентов, но и функции, размещенные в секции PHP страниц и лейаутов (но не паршлов).
То есть мы можем сделать в htm-файле страницы вот так:
url = "/blog/posts"
[blogPosts allPosts]
pageNumber = 1
postsPerPage = 12
noPostsMessage = "No posts found"
sortOrder = "published_at desc"
==
function onLoadMore()
{
// загрузить еще постов
}
==
<div class="blog-list">
{% partial 'blogPosts' %}
</div>
И функцию onLoadMore()
можно будет использовать как обработчик ajax-события. Тогда мы сможем сделать кнопку, не трогая плагин и не умножая сущности.
Допустим у нас есть такая разметка:
<div>
{% component 'allPosts' %}
</div>
<div id="load-more-button">
<a data-request="onLoadMore" data-request-data="page: 1" href="#">Load More</a>
</div>
Компонент allPosts
— это лента постов Posts
из плагина RainLab.Blog
. Кнопку загрузки мы выводим вне компонента с постами, а сам компонет выглядит как-нибудь так:
<div id="all-posts-content" class="row">
{% for post in posts %}
<!-- Здесь выводятся посты, как обычно -->
{% endfor %}
</div>
По клику на кнопку Load More на сервере будет вызвана функция onLoadMore
, которую мы добавили в php-секцию нашей страницы:
function onLoadMore() {
// получаем номер страницы
$pageNumber = Input::get('page') + 1;
// обращаемся к компоненту Posts с alias allPosts:
// выставляем номер страницы и готовим данные
$this->components['allPosts']->setProperty('pageNumber', $pageNumber);
$this->components['allPosts']->onRun();
if ($pageNumber < $this->components['allPosts']->posts->lastPage()) {
// для простоты мы генерируем html, но в принципе можно сделать и отдельный паршл для кнопки
$more_link = '<a data-request="onLoadMore" data-request-data="page: ' . $pageNumber . '" href="#">Load More</a>';
} else {
// если мы достигли последней страницы, кнопка больше не нужна
$more_link = '';
}
return [
// если перед селектором стоит @, новое содержимое будет добавляться в конец, а не заменять старое
'@#all-posts-content' => $this->components['allPosts']->renderPartial('@default.htm'),
'#load-more-button' => $more_link,
];
}
Обратите внимание, что на сервер будут переданы данные из data-request-data
, то есть номер страницы. Он используется, чтобы можно было нажимать на кнопку многократно и подгружать следующие блоки данных. Для этого мы в конце и код самой кнопки перегружаем — чтобы новый номер страницы вывести.
Вот и всё. Остальное за нас делает OctoberCMS.