Yii2: Вникаем в Pjax

regular request vs pjax requestПопулярный ныне 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

Вы можете скачать исходные коды примеров к этой статье с битбакета.

Обновление данных

Давайте рассмотрим самый простой пример. Допустим, на странице имеются какие-нибудь данные, которые нужно обновлять при нажатии ссылки. Для примера, отобразим текущее время на сервере. Демонстрация.

обновление данных yii2 и pjax

Сначала рассмотрим представление и ту часть его, что нужно обновлять динамически. Используя теги виджета <?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’.

Навигация

В данном примере мы рассмотрим использование нескольких ссылок на разные контроллеры, возвращающие разные данные. Код аналогичен первому примеру, за исключением того, что здесь используются две кнопки. Рабочий пример.

обновление данных yii2 и pjax 02

<?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. Они будут работать независимо друг от друга и обновлять только данные внутри себя. Рабочий пример.

обновление данных yii2 и 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 — отобразим хеш строки из формы. Рабочий пример.

обновление данных 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 в браузере не изменится. Рабочий пример.

обновление данных yii2 и pjax

Представление содержит только две ссылки и счетчик. Для упрощения примера, значение счетчика будем хранить в сессии. Реальные системы голосования устроены на много сложнее, но эта тема выходит за рамки этой статьи. Для отключения 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 в представлении, никакие изменения контроллера не понадобятся. Рабочий пример.

yii2 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.

15 thoughts on “Yii2: Вникаем в Pjax

  1. Вячеслав

    Спасибо! Мануал то что надо. Всё просто, оказывается:)
    Буду пробовать

  2. Игорь

    Здравствуйте!
    А как использовать pjax внутри собственного виджета?
    У меня имеется страница, которая генерирует в предсавлении публикует множество разных виджетов. Разные виджеты имеют или не имеют свои pjax обертки с разными обработчиками при нажатии на ссылку для обновления.

    Но ведь у виджетов нет контроллеров, которые я мог бы вызвать через ссылку.

    А впихивать акшены в контроллеры на все типы виджетов — недопустимо, не для того я виджеты создавал, чтобы логику их работы опять описывать в контроллере.

  3. stalkermiha

    Обновление страницы яваскриптом не актуально

    «В случае необходимости обновления данных на странице при наступлении какого-нибудь события или по таймеру, нужно добавить, всего лишь, пару строк js кода.»

    Это можно сделать html тегом:

    Вот пример для простой перезагрузки страницы, через определенный интервал времени:

    где 10 — интервал обновления страницы в секундах.
    Вот пример для перевода посетителя на другую страницу (сайт), через определенное время:

    1. Александр

      Всмысле не актуально? HTML вообще не должен определять поведение, его задача только представление информации. Такие вещи как редирект и обновление страницы или ее части — поведение.

      Кроме того в статье вообще идет речь об обновлении отдельного элемента страницы, без ее полной перезагрузки.

      Вещь надо сказать, тоже не слишком передовая, с учетом всяких Backbone и Angular, которые гоняют между клиентом и сервером только данные в json, но тоже имеет право на жизнь, так как весьма быстро реализуемаю

  4. stalkermiha

    в комментах ограничение на колво символов ? а где это указанно? продолжу:

    где после URL указывается страница (сайт), куда нужно перенаправить посетителя.

  5. Максим

    Все заработало, благодаря опции data-pjax! Спасибо большое! Но почему не выводится флеш?

    true]) ?>

    session->hasFlash(‘success’) ? Yii::$app->session->getFlash(‘success’) : null ?>

  6. Fedor

    У кого-нибудь получилось генерировать отдельно строку и ключ в примере «Несколько независимых блоков»? Можете кинуть код посмотреть, а то я не совсем понял, как это сделать.

  7. Иван

    Скажите, что делать, если страница обновляется?
    Если указываю url — ‘site/index’
    Если просто ‘#’ — не обновляется страница, но и данные не обновляются.

  8. Виталик

    Есть простая форма,.. обернул её в pjax …
    заполнил форму.. все пошло красиво без перегруза.
    Заполнил форму снова и почему то уже есть перегрузка страницы.

  9. Дмитрий

    А как сделать, чтобы при вводе данных в поле Input выводились ти данные в режиме реального времени в другом месте?

  10. Петр

    Тоже вопрос по примеру «Несколько независимых блоков»
    При организации нескольких pjax (в блоке кнопка для дозагрузки) корректно работает только первый, остальные вызывают перезагрузку страницы (и уход с нее т.к. у меня используется другой контроллер) + в блок рендерится весь код вьюхи. Как у Вас получилась для разных виджетов одна вьюха?

  11. MasterCard

    «Популярный ныне js фреймворк jQuery»

    фреймворк?) это всего лишь библиотека, Карл!

  12. Дмитрий

    Я не понял, как виджет обеспечивает рендер куска шаблона на стороне сервера.
    Он каким-то образом влияет на поведение шаблонизатора, чтобы он исполнил только часть представления, заключенную внутри pajax тегов?
    Или ендер представления происходит весь, вместе с лейаутами, а pajax из готовой страницы вырезает свой блок и возвращает только его?

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *