Yii2 Pjax Tutorials with examples

Yii2 Pjax Tutorial

pjax is a jQuery plugin that allows quick website navigation by combining ajax and pushState. It works by sending a special request to the server every time a link is clicked or a form is submitted. The server sends back the content that needs to be updated, pjax replaces old content with new content and pushes the new URL to browser history, without the need to update the whole page.

Regular Request vs Pjax Request Schematic
Regular requests download the whole page. Pjax can ask the server to send only the required part of the content.

Yii2 framework has a Pjax widget that helps you pjaxify your website in just one easy step.

  1. Add one line in the beginning of your view.
    <?php
    use yii\widgets\Pjax;
    ?>
  2. Add two lines around the content that needs partial updating.
    <?php Pjax::begin(); ?>
    Content that needs to be updated
    <?php Pjax::end(); ?>

You can further configure the widget. Choose which links and which forms will be hadled by Pjax; whether the new URL should be pushed to browser history, replaced or left as is; the timeout after which the page will be reloaded in case there’s no response; Pjax can also scroll the page for you.

Yii2 Pjax Examples

You can download the source code of the examples from this tutorial from GitHub.

Refresh

Let’s look at the simplest example first. We have some data on our page that we want to refresh by clicking a link. For simplicity’s sake we’ll use the current server time as our dynamic data. Demo.

Yii2 Pjax Example - Refresh buttonFirst lets tell the view which part of the page we want to update dynamically. This is achieved by placing the widgets tags <?php Pjax::begin(); ?> and <?php Pjax::end(); ?>  around the content. Every link and every form between these tags will now trigger a pjax request.

<?php Pjax::begin(); ?>
<?= Html::a("Refresh", ['site/index'], ['class' => 'btn btn-lg btn-primary']) ?>
<h1>Current time: <?= $time ?></h1>
<?php Pjax::end(); ?>

Our action only provides the $time to the view and renders the view.

public function actionIndex()
{
    return $this->render('index', ['time' => date('H:i:s')]);
}

That’s it. Whenever the widget will detect a pjax request it will serve the required part of the content instead of the whole page.

Don’t forget to add use yii\widgets\Pjax; in your view.

Auto refresh

While we’re on topic of refreshing lets take our previous code and make it refresh the content automatically after a number of seconds. Demo. To achieve this we’ll add a couple of lines of javascript to our view.

<?php
$script = <<< JS
$(document).ready(function() {
    setInterval(function(){ $("#refreshButton").click(); }, 3000);
});
JS;
$this->registerJs($script);
?>

This code will trigger a click event for our refresh button every three seconds. Note that we need to specify an id ‘id’ => ‘refreshButton’  for our button for this to work. If you want to you can hide the button by setting its CSS class to hidden ‘class’ => ‘hidden’.

Navigation

In this example we will have several links pointing to different controller actions that will return different results. Our view will look almost the same as in the previous examples, except now it will have two links/buttons. Demo.

Yii2 Pjax Example - Navigation

<?php Pjax::begin(); ?>
<?= Html::a("Show Time", ['site/time'], ['class' => 'btn btn-lg btn-primary']) ?>
<?= Html::a("Show Date", ['site/date'], ['class' => 'btn btn-lg btn-success']) ?>
<h1>It's: <?= $response ?></h1>
<?php Pjax::end(); ?>

Here are the two actions that will render the different views. It’s as simple as that.

public function actionTime()
{
    return $this->render('time-date', ['response' => date('H:i:s')]);
}

public function actionDate()
{
    return $this->render('time-date', ['response' => date('Y-M-d')]);
}

Multiple blocks

You can use the widget in several places on one page. When a link inside of one of these blocks will be clicked it only that block will be updated. Demo.

Yii2 Pjax Example - Multiple Widgets

 

Here is our view. Notice how the widget is used in two places inside one view.

<div class="col-sm-12 col-md-6">
    <?php Pjax::begin(); ?>
    <?= Html::a("Generate Random String", ['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("Generate Random Key", ['site/multiple'], ['class' => 'btn btn-lg btn-primary']) ?>
    <h3><?= $randomKey ?><h3>
    <?php Pjax::end(); ?>
</div>

Here is the corresponding action. We will also require yii\base\Security for this example.

public function actionMultiple()
{
    $security = new Security();
    $randomString = $security->generateRandomString();
    $randomKey = $security->generateRandomKey();
    return $this->render('multiple', [
        'randomString' => $randomString,
        'randomKey' => $randomKey,
    ]);
}

This isn’t the best solution. Every time one of the buttons is clicked both, the random string and the random key, are generated, but we use only one of them for the purpose of updating the view. To fix this we can add two more actions and two additional child views, each for the corresponding function (string generation and key generation). Then we could call the child views from our main view <?= $this->render(‘_randomKey’, [‘randomKey’ => $randomKey]); ?>  and render them directly from our additional actions.

Form submission

Submitting a form is a great use case for the Pjax widget. For this example we will be hashing strings so it will take time for the server to respond. Demo.

Yii2 Pjax Example - Form Submission
Our view will include the Pjax widget, a form, a text input field and a submit button.

<?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('Hash String', ['class' => 'btn btn-lg btn-primary', 'name' => 'hash-button']) ?>
<?= Html::endForm() ?>
<h3><?= $stringHash ?></h3>
<?php Pjax::end(); ?>

Here is our action. Nothing out of the ordinary.

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,
    ]);
}

Since our forms method is POST, pushState won’t trigger and the URL won’t be updated.

Disabling pushState

Sometimes you will want to disable pushState manually. In this example part of the view will be updated, but the URL will stay the same. Demo.

yii2-pjax-example-disable-pushstate

The view will only have two links and the vote count. To make this simple we will just store the number of votes in the session. Voting systems are beyond the scope of this tutorial. To configure the widget we will pass an array as a parameter in its begin method.

<?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(); ?>

The controller will feature three actions. One will simply render the view. The other two actions will process the upvote and downvote pjax requests.

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 sorting and pagination

Yii2 GridView has been specifically designed to allow seamless sorting, filtering and pagination with the Pjax widget. Simply put the GridView widget inside the Pjax widget in your view. No modifications for the controller are necessary. Demo.

yii2-pjax-gridview-example

 

<?php Pjax::begin(); ?>
<?= GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],

        'id',
        'branch:ntext',
        'version:ntext',
        'release_date:ntext',

        [
            'class' => 'yii\grid\ActionColumn',
            'template' => '{view}',
        ],
    ],
]); ?>
<?php Pjax::end(); ?>

Conclusion

The Pjax widget is great for some use cases. If something goes wrong or the request will take too much time it can reload the page completely. You can still open links in new tabs or windows. It’s a great replacement for CHtml::ajaxLink from Yii 1.x. Search engines don’t have to render JavaScript to crawl websites that utilize pjax. You can customize it further using JavaScript.

It’s not very efficient because it has to send the required data alongside with HTML code. Depending on your needs, you can make a more efficient web application using a JavaScript MVC framework(e.g. AngularJS), jQuery or even plain JavaScript. All you have to do is separate the views from the data, then, with the help of Ajax, deliver the views as HTML and the data as JSON.

Published by

Alexander

Your friendly, neighborhood, full stack, pseudorandom text generator

28 thoughts on “Yii2 Pjax Tutorial”

    1. The short answer is: there’s no difference.
      When the view renderer gets to the Pjax widget, the widget checks if the request has a X-Pjax header (so we don’t have to). It also checks if the X-Pjax-Container header id corresponds to the id of the widget and if so the contents of that widget will be rendered, returned as a response and no further rendering will be done.
      The DOM acknowledges the updated content.

  1. Hi, thank you for clear tutorial.

    I try to use Pjax inside Modal.
    I have buttons inside Modal and I wish after action is executed (after pressing button) result to be returned in Modal, Modal stays open and url not to be changed.

    What I have is that result is returned but my modal get closed and url is changed (although I put ‘enablePushState’ => false). View is rendered but not inside Modal.

    Can someone suggest what the problem is?

  2. Hi again, I think I found the solution. I will share it to benefit someone.

    Inside Modal I have ActiveForm. I enclose the whole ActiveForm inside Pjax:

    Pjax::begin([‘enablePushState’ => false]);

    …..declaration of my form with fields and button links….

    Pjax::end;

    Also I put this property ‘data-pjax’ => true inside form options:

    $form = ActiveForm::begin([
    ‘id’ => ‘myForm’,
    ‘options’ => [‘data-pjax’ => true],
    …..
    ]);

  3. Hi, thanks for this tutorial. I have already try it, I using pjax with gridview pagination and it works really well but i want to automatically going to next page and next again at every minute, can it be done using pjax? how to do so? thanks

    1. Hi, yes, it’s not hard. You can take the code from the auto refresh example and change it a little. If you are using a standard gridview this should work:

      < ?php $script = <<< JS $(document).ready(function() { setInterval(function(){ $(".next a").click(); }, 60000); }); JS; $this->registerJs($script);
      ?>

      I changed the jQuery selector to target the next page link/button and changed the 3 second interval to 60 seconds.
      Note that, when it will reach the last page, it will stop.

  4. Great tutorial, thanks.

    Could you also provide the correct Multiple Blocks example (with the two extra child views and actions)?

    In your example you have two blocks that are both shown in the same view (multiple).
    I don’t understand how to use multiple widgets without affecting this.

    1. It works, but there’s a catch. If the reply takes too long, or the server reply lacks the X-PJAX header then the whole page is loaded. The timeout can be configured. So if you clicked multiple times quickly, or there were too many visitors online – you could have witnessed a the whole page reloading.

  5. How to use Pjax with create/update forms?
    I have one dropdown with provinces and another with districts. I’d like to update districts list to only show those from selected province.

    I have inside view:

    field($model, ‘id_province’)->dropDownList(
    ArrayHelper::map(Province::find()->all(),’id_province’,’province’),
    [‘prompt’=>’wybierz’, ‘onchange’=>Url::current()]) ?>

    field($model, ‘id_district’)->dropDownList($district_options, [‘prompt’=>’wybierz’]) ?>

    And inside controller:

    public function actionUpdate($id)
    {
    $model = $this->findModel($id);

    if ($model->load(Yii::$app->request->post()) && $model->save()) {
    return $this->redirect([‘view’, ‘id’ => $model->id_borough]);
    } else {

    $district_options = ArrayHelper::map(District::find()->where($model->id_province>0 ? ‘id_province=’.$model->id_province : ‘1=1′)->all(),’id_district’,’district’);

    return $this->render(‘update’, [
    ‘model’ => $model, ‘district_options’ => $district_options
    ]);
    }
    }

    It works on first load, but updating provinces list doesn’t even trigger controller (and i guess it will not work in this way, need extra action for this?)

  6. Hi. Thanks for this tutorial. But it has one inaccuracy:

    > Every link and every form between these tags will now trigger a pjax request.

    From official documentation:

    > You can configure $formSelector to specify which form submission may trigger pjax. If not set, all forms with “data-pjax” attribute within the enclosed content of Pjax will trigger pjax requests.

    http://www.yiiframework.com/doc-2.0/guide-input-forms.html#working-with-pjax

    For the forms we must specify “data-pjax” attribute!

  7. Hi, thank for the tutorial…. Well I’m working on Yii2 framework.. I need to know can I update the database on selecting the value from the drop down without refreshing the page ???

    1. You’re welcome. I haven’t used pjax since this tutorial and I’m not planning to, but it can be used for very simple things. If I were to choose a js library for small or large project it would be Vue.js. It’s very simple and fast, the learning curve is more attractive than for other js frameworks IMO and the documentation is a joy to read.

  8. Hi, thanks for your tutorial. I have one doubt how to do search form with ajax in index page.Now i try to search but search field passed in URL.I want to search form in ajax.How to do.Kindly give some idea.

Leave a Reply