Популярный ныне js фреймворк jQuery оброс большим количеством разнообразных плагинов. Одним из таких плагинов является pjax, позволяющий легко создавать веб приложения с использованием связки ajax и pushState. Эта технология позволяет после нажатия ссылки или submit на форме, отправить на сервер специальный запрос и получить в ответ только то содержимое, которое необходимо обновить на странице, затем pjax заменяет старое содержимое новым и добавляет в историю браузера и адресную строку актуальную url ссылку, без обновления всей страницы.
Обычный запрос каждый раз полностью перезагружает страницу, а pjax может запросить у сервера только нужную часть содержимого.
Фреймворк Yii 2.0 содержит pjax виджет, использование которого не должно вызвать затруднений даже у начинающих.
Сначала укажем пространство имен виджета pjax.
<?php use yii\widgets\Pjax; ?>
Теперь достаточно обернуть нужную область виджетом.
<?php Pjax::begin(); ?> // Содержимое, которое нужно обновлять динамически <?php Pjax::end(); ?>
Так же, возможна настройка виджета:
- выбор ссылок или форм, которые будут обрабатываться через pjax;
- указание url ссылки, которая должна быть помещена в историю и адресную строку браузера;
- период времени, через который страница должна быть обновлена целиком, в случае отсутствия ответа;
- прокрутка страницы, в случае необходимости.
Примеры использования pjax в yii2
Вы можете скачать исходные коды примеров к этой статье с битбакета.
Обновление данных
Давайте рассмотрим самый простой пример. Допустим, на странице имеются какие-нибудь данные, которые нужно обновлять при нажатии ссылки. Для примера, отобразим текущее время на сервере. Демонстрация.
Сначала рассмотрим представление и ту часть его, что нужно обновлять динамически. Используя теги виджета <?php Pjax::begin(); ?> и <?php Pjax::end(); ?>, указываем динамическое содержимое. Теперь любая ссылка или форма внутри виджета будет обрабатываться через pjax.
<?php Pjax::begin(); ?> <?= Html::a("Обновить", ['site/index'], ['class' => 'btn btn-lg btn-primary']) ?> <h1>Сейчас: <?= $time ?></h1> <?php Pjax::end(); ?>
Действие index только лишь формирует данные $time для отображения в представлении.
public function actionIndex() { return $this->render('index', ['time' => date('H:i:s')]); }
Не забывайте добавить use yii\widgets\Pjax; в начале файла представления.
Автоматическое обновление данных на странице
В случае необходимости обновления данных на странице при наступлении какого-нибудь события или по таймеру, нужно добавить, всего лишь, пару строк js кода. Рабочий пример.
<?php $script = <<< JS $(document).ready(function() { setInterval(function(){ $("#refreshButton").click(); }, 3000); }); JS; $this->registerJs($script); ?>
Этот код будет имитировать нажатие кнопки «Обновить» на странице каждые три секунды. Нужно только указать идентификатор кнопки ‘id’ => ‘refreshButton’. При необходимости, можно скрыть кнопку, указав соответствующий css класс: ‘class’ => ‘hidden’.
Навигация
В данном примере мы рассмотрим использование нескольких ссылок на разные контроллеры, возвращающие разные данные. Код аналогичен первому примеру, за исключением того, что здесь используются две кнопки. Рабочий пример.
<?php Pjax::begin(); ?> <?= Html::a("Показать дату", ['site/date'], ['class' => 'btn btn-lg btn-success']) ?> <?= Html::a("Показать время", ['site/time'], ['class' => 'btn btn-lg btn-primary']) ?> <h1>Сейчас: <?= $response ?></h1> <?php Pjax::end(); ?>
Два представления отображают одно представление с разными данными.
public function actionTime() { return $this->render('time-date', ['response' => date('H:i:s')]); } public function actionDate() { return $this->render('time-date', ['response' => date('d.m.Y')]); }
Несколько независимых блоков
При необходимости, можно разместить на странице несколько виджетов pjax. Они будут работать независимо друг от друга и обновлять только данные внутри себя. Рабочий пример.
Рассмотрим представление. Два pjax виджета легко уживаются по-соседству в одном представлении.
<div class="col-sm-12 col-md-6"> <?php Pjax::begin(); ?> <?= Html::a("Новая случайная строка", ['site/multiple'], ['class' => 'btn btn-lg btn-primary']) ?> <h3><?= $randomString ?></h3> <?php Pjax::end(); ?> </div> <div class="col-sm-12 col-md-6"> <?php Pjax::begin(); ?> <?= Html::a("Новый случайный ключ", ['site/multiple'], ['class' => 'btn btn-lg btn-primary']) ?> <h3><?= $randomKey ?><h3> <?php Pjax::end(); ?> </div>
И соответствующее действие.
public function actionMultiple() { $security = new Security(); $randomString = $security->generateRandomString(); $randomKey = $security->generateRandomKey(); return $this->render('multiple', [ 'randomString' => $randomString, 'randomKey' => $randomKey, ]); }
В целях упрощения примера, приведен не самый оптимальный образец кода. В данном случае, при нажатии на любую из кнопок, действие генерирует и хеш и ключ, и передает оба этих значения в представление. Можно использовать два действия и два дополнительных дочерних представления. Тогда вызывая дополнительное представление из основного <?= $this->render(‘_randomKey’, [‘randomKey’ => $randomKey]); ?>, мы отобразим их из соответствующего действия.
Pjax формы в Yii2
Еще один отличный пример — использование форм совместно с pjax — отобразим хеш строки из формы. Рабочий пример.
В представлении форма с текстовым полем и кнопкой обернуты виджетом pjax.
<?php Pjax::begin(); ?> <?= Html::beginForm(['site/form-submission'], 'post', ['data-pjax' => '', 'class' => 'form-inline']); ?> <?= Html::input('text', 'string', Yii::$app->request->post('string'), ['class' => 'form-control']) ?> <?= Html::submitButton('Получить хеш', ['class' => 'btn btn-lg btn-primary', 'name' => 'hash-button']) ?> <?= Html::endForm() ?> <h3><?= $stringHash ?></h3> <?php Pjax::end(); ?>
В действии тоже нет ничего необычного.
public function actionFormSubmission() { $security = new Security(); $string = Yii::$app->request->post('string'); $stringHash = ''; if (!is_null($string)) { $stringHash = $security->generatePasswordHash($string); } return $this->render('form-submission', [ 'stringHash' => $stringHash, ]); }
Отключение pushState
В некоторых случаях, необходимо вручную отключить pushState. В данном случае, данные будут обновлены, а url в браузере не изменится. Рабочий пример.
Представление содержит только две ссылки и счетчик. Для упрощения примера, значение счетчика будем хранить в сессии. Реальные системы голосования устроены на много сложнее, но эта тема выходит за рамки этой статьи. Для отключения pushState, указываем в параметрах виджета в открывающем методе ‘enablePushState’ => false.
<?php Pjax::begin(['enablePushState' => false]); ?> <?= Html::a('', ['site/upvote'], ['class' => 'btn btn-lg btn-warning glyphicon glyphicon-arrow-up']) ?> <?= Html::a('', ['site/downvote'], ['class' => 'btn btn-lg btn-primary glyphicon glyphicon-arrow-down']) ?> <h1><?= Yii::$app->session->get('votes', 0) ?></h1> <?php Pjax::end(); ?>
Контроллер содержит три действия. Первое просто отображает представление. Другие два изменяют значение счетчика в сессии и отображают представление.
public function actionVote() { return $this->render('vote'); } public function actionUpvote() { $votes = Yii::$app->session->get('votes', 0); Yii::$app->session->set('votes', ++$votes); return $this->render('vote'); } public function actionDownvote() { $votes = Yii::$app->session->get('votes', 0); Yii::$app->session->set('votes', --$votes); return $this->render('vote'); }
Сортировка и пагинация gridview в yii2 используя pjax
Виджет GridView в Yii 2.0 был создан с расчетом на динамическое обновление данных, сортировку и пагинацию с использованием pjax. Достаточно просто обернуть gridview в pjax в представлении, никакие изменения контроллера не понадобятся. Рабочий пример.
<?php Pjax::begin(); ?> <?= GridView::widget([ 'dataProvider' => $dataProvider, 'filterModel' => $searchModel, 'layout' => '{summary}{items}{pager}', 'summary' => 'Показаны записи <strong>{begin}-{end}</strong> из <strong>{totalCount}</strong>.', 'columns' => [ ['class' => 'yii\grid\SerialColumn'], 'id', 'branch:ntext', 'version:ntext', 'release_date:ntext', [ 'class' => 'yii\grid\ActionColumn', 'template' => '{view}', ], ], ]); ?> <?php Pjax::end(); ?>
Заключение
Виджет pjax очень полезен в большинстве случаев. Если с запросом пойдет что-то не так, он может просто перезагрузить страницу целиком. Ссылки на странице можно по-прежнему открывать в новом окне. Pjax стал отличной заменой CHtml::ajaxLink из Yii первой версии.
В тоже время, методика работы pjax не оптимальна, он оперирует тяжелыми запросами с html кодом. При необходимости, для создания более легких и быстрых веб приложений, нужно использовать js mvc феймворки (например angularjs), jQuery или чистый js. Для этого достаточно отделить данные от представлений при помощи ajax.
Спасибо! Мануал то что надо. Всё просто, оказывается:)
Буду пробовать
Здравствуйте!
А как использовать pjax внутри собственного виджета?
У меня имеется страница, которая генерирует в предсавлении публикует множество разных виджетов. Разные виджеты имеют или не имеют свои pjax обертки с разными обработчиками при нажатии на ссылку для обновления.
Но ведь у виджетов нет контроллеров, которые я мог бы вызвать через ссылку.
А впихивать акшены в контроллеры на все типы виджетов — недопустимо, не для того я виджеты создавал, чтобы логику их работы опять описывать в контроллере.
Обновление страницы яваскриптом не актуально
«В случае необходимости обновления данных на странице при наступлении какого-нибудь события или по таймеру, нужно добавить, всего лишь, пару строк js кода.»
Это можно сделать html тегом:
Вот пример для простой перезагрузки страницы, через определенный интервал времени:
где 10 — интервал обновления страницы в секундах.
Вот пример для перевода посетителя на другую страницу (сайт), через определенное время:
Всмысле не актуально? HTML вообще не должен определять поведение, его задача только представление информации. Такие вещи как редирект и обновление страницы или ее части — поведение.
Кроме того в статье вообще идет речь об обновлении отдельного элемента страницы, без ее полной перезагрузки.
Вещь надо сказать, тоже не слишком передовая, с учетом всяких Backbone и Angular, которые гоняют между клиентом и сервером только данные в json, но тоже имеет право на жизнь, так как весьма быстро реализуемаю
в комментах ограничение на колво символов ? а где это указанно? продолжу:
где после URL указывается страница (сайт), куда нужно перенаправить посетителя.
Все заработало, благодаря опции data-pjax! Спасибо большое! Но почему не выводится флеш?
true]) ?>
session->hasFlash(‘success’) ? Yii::$app->session->getFlash(‘success’) : null ?>
У кого-нибудь получилось генерировать отдельно строку и ключ в примере «Несколько независимых блоков»? Можете кинуть код посмотреть, а то я не совсем понял, как это сделать.
код приведен в примере https://nix-tips.ru/examples/yii2pjax/multiple
исходный код приложения https://bitbucket.org/p0vidl0/yii2-examples
Скажите, что делать, если страница обновляется?
Если указываю url — ‘site/index’
Если просто ‘#’ — не обновляется страница, но и данные не обновляются.
Есть простая форма,.. обернул её в pjax …
заполнил форму.. все пошло красиво без перегруза.
Заполнил форму снова и почему то уже есть перегрузка страницы.
А как сделать, чтобы при вводе данных в поле Input выводились ти данные в режиме реального времени в другом месте?
Это уже надо WebSockets мутить! Чтобы сигнал с сервера приходил.
Тоже вопрос по примеру «Несколько независимых блоков»
При организации нескольких pjax (в блоке кнопка для дозагрузки) корректно работает только первый, остальные вызывают перезагрузку страницы (и уход с нее т.к. у меня используется другой контроллер) + в блок рендерится весь код вьюхи. Как у Вас получилась для разных виджетов одна вьюха?
«Популярный ныне js фреймворк jQuery»
фреймворк?) это всего лишь библиотека, Карл!
Я не понял, как виджет обеспечивает рендер куска шаблона на стороне сервера.
Он каким-то образом влияет на поведение шаблонизатора, чтобы он исполнил только часть представления, заключенную внутри pajax тегов?
Или ендер представления происходит весь, вместе с лейаутами, а pajax из готовой страницы вырезает свой блок и возвращает только его?