This is an old revision of the document!


Yii 2 Using AJAX
Using JQuery AJAX

Create a view. This should include the JavaScript method to call AJAX, and a form control that calls the method somehow (eg. onchange or click events).

File /views/issue/create.php:

<?php
// Add Javascript code to perform AJAX call (using JQuery)
$jsBlock = "
function getCompany()
{
    $.ajax({
            // Controller method to call
            url: '" . Yii::$app->request->baseUrl. '/issue/get-company' . "',
 
            // Parameter data to pass in
            data: {
                account_number: $('#issue-account_number').val(),
            },
 
            type: 'POST',
 
            cache: false,
 
            // Code to run if the request succeeds;
            // the response is passed to the function
            success: function(data) {
              // process data
              console.log('data: [' + data + ']');
              $('#company_search_results').html(data);
            },
 
            // Code to run if the request fails; 
            // the raw request and status codes are passed to the function
            error: function( xhr, status, errorThrown ) {
                alert('Sorry, there was a problem! Error: ' + errorThrown + 'Status: ' + status + xhr);
                console.log('Error: ' + errorThrown );
                console.log('Status: ' + status );
                console.dir( xhr );
            },
 
            // Code to run regardless of success or failure
            complete: function( xhr, status ) {
                //alert('The request is complete!');
            }
    })
    .fail (function(){ alert('Error'); });
}
";
 
// NOTE: $position can be:
//   View::POS_READY (the default)
//   View::POS_HEAD 
//   View::POS_BEGIN
//   View::POS_END
$position = \yii\web\View::POS_END;
 
$this->registerJs($jsBlock, $position);
?>
...    
<div class="issue-form">
    <?php $form = ActiveForm::begin(); ?>
    <!-- Field to grab parameter to pass to AJAX query -->
    <?= $form->field($model, 'account_number')->textInput([
        'maxlength' => true, 
        'onchange'=>'getCompany()'
    ]) ?>
    <p/>
    <!-- Box to display AJAX results -->
    <?= Html::tag('div', '...', ['id' => 'company_search_results']); ?>
    ...
</div>
 

In controller, add action to handle query in OnChange: File /controllers/IssueController.php:

use app\models\Issue;
use app\models\Customer;
...
class IssueController extends Controller
{
    ...
 
    public function actionGetCompany()
    {
        // Grab AJAX parameter from POST (since that is the method we used)
        if (Yii::$app->request->isPost){
            $account_number = Yii::$app->request->post('account_number');
            Yii::info('POST account_number: ' . $account_number, 'actionGetrec');
        }
 
        // Validate and query the data we need
        if (empty($account_number)) {
            $modelCust = new Customer(); 
            $account_number = $modelCust->account_number;
            $company_name   = $modelCust->company_name;
            $arrCustomer    = $modelCust->toArray();
            Yii::info('Empty account_number: ' . $account_number . 
                      'company_name: ' . $company_name, 'actionGetrec');
        } else {
            $modelCust    = Customer::find()->where(['account_number' => $account_number])->one();
            $arrCustomer  = Customer::find()->where(['account_number' => $account_number])->asArray()->one();
            if(!empty($modelCust)) {
                $company_name = $modelCust->company_name;
                Yii::info('Found account_number: ' . $account_number . 
                          'company_name: ' . $company_name, 'actionGetrec');
                Yii::info('Customer record: ' . print_r($modelCust, true), 'actionGetrec');
            } else {
                Yii::info('Customer not found: ' . $account_number, 'actionGetrec');
            }
        }
 
        // Prepare data to send back
        $data = array();
        if (!empty($arrCustomer['company_name']))   { $data[] = $arrCustomer['company_name']; }
        if (!empty($arrCustomer['address']))        { $data[] = $arrCustomer['address']; }
        if (!empty($arrCustomer['city']))           { $data[] = $arrCustomer['city']; }
        if (!empty($arrCustomer['state_prov']))     { $data[] = $arrCustomer['state_prov']; }
        if (!empty($arrCustomer['country']))        { $data[] = $arrCustomer['country']; }
        if (!empty($arrCustomer['phone']))          { $data[] = $arrCustomer['phone']; }
        if (!empty($arrCustomer['phone']))          { $data[] = $arrCustomer['phone']; }
        if (!empty($arrCustomer['email']))          { $data[] = $arrCustomer['email']; }
 
        // Send AJAX results
        if (count($data) > 0) {
            return '<div class="alert alert-success" role="alert">' . implode(', ', $data) . '</div>';
        } else {
            return '<div class="alert alert-danger" role="alert">Customer record not found</div>';
        }
    }
}    
Using JQuery $.get()

You can also get data without using AJAX by simply using this JQuery $.get() method attached to an event (such as OnChange event):

<?= $form->field($model, 'product_name')->dropDownList(ArrayHelper::map(Products::find()->all(), 'id', 'name'), 
    ['prompt'=>'-Choose a Product-',
         'onchange'=>'
         $.get( "index.php?r=suborders/listprice&id="+$(this).val(), function( data ) {
            $( "#suborders-product_price" ).val( data );
         });
    ']);
?>

Examples

Request the test.php page, but ignore the return results.

$.get( "test.php" );

Request the test.php page and send some additional data along (while still ignoring the return results).

$.get( "test.php", { name: "John", time: "2pm" } );

Pass arrays of data to the server (while still ignoring the return results).

$.get( "test.php", { "choices[]": ["Jon", "Susan"] } );

Alert the results from requesting test.php (HTML or XML, depending on what was returned).

$.get( "test.php", function( data ) {
  alert( "Data Loaded: " + data );
});

Alert the results from requesting test.cgi with an additional payload of data (HTML or XML, depending on what was returned).

$.get( "test.cgi", { name: "John", time: "2pm" } )
  .done(function( data ) {
    alert( "Data Loaded: " + data );
  });

Get the test.php page contents, which has been returned in json format (<?php echo json_encode( array( “name”⇒“John”,“time”⇒“2pm” ) ); ?>), and add it to the page.

$.get( "test.php", function( data ) {
  $( "body" )
    .append( "Name: " + data.name )  // John
    .append( "Time: " + data.time ); //  2pm
}, "json" );

See more:

Using AJAX Returning JSON

Add HTML elements that will be updated with AJAX, and attach the AJAX functionality to an event, such as the click event. Eg: The view index for the SiteController (file [app]/views/site/index.php):

<h2>Quote of the day</h2>
<div id="quote-of-the-day">
    Click to get random quote
</div>
<div id="quote-number-2">
    Click to get quote ID 2
</div>
<div id="quote-error">
    Click to get quote that does not exist
</div>
 
...
 
<?php
// Javascript code block to handle AJAX requests
$jsBlock = <<< JS
    // get without 'query' = random quote
    $('#quote-of-the-day').click(function(){
        $.ajax({
            //url: '?r=professional/quote',   // using standard routing
            url: 'quote',                     // using PrettyUrl routing
            dataType: "json",
            success: function(data) {
                if(data.error) {
                    alert(data.error);
                } else if(data.quote) {
                    $("#quote-of-the-day").html(data.quote);
                } else {
                    $("#quote-of-the-day").html("Response in invalid format!");
                    alert("Response in invalid format!");
                }
            }
        })
    });
 
    // get with ID 2
    $('#quote-number-2').click(function(){
        $.ajax({
            //url: '?r=professional/quote&query=2',   // using standard routing
            url: 'quote/?query=2',                    // using PrettyUrl routing
            dataType: "json",
            success: function(data) {
                if(data.error) {
                    alert(data.error);
                } else if(data.quote) {
                    $("#quote-number-2").html(data.quote);
                } else {
                    $("#quote-number-2").html("Response in invalid format!");
                    alert("Response in invalid format!");
                }
            }
        })
    });
 
    // get with ID 5 - does not exist
    $('#quote-error').click(function(){
        $.ajax({
            //url: '?r=professional/quote&query=5',   // using standard routing
            url: 'quote/?query=5',                    // using PrettyUrl routing
            dataType: "json",
            success: function(data) {
                if(data.error) {
                    alert(data.error);
                } else if(data.quote) {
                    $("#quote-error").html(data.quote);
                } else {
                    $("#quote-error").html("Response in invalid format!");
                    alert("Response in invalid format!");
                }
            }
        })
    });
JS;
 
// NOTE: $position can be:
//   View::POS_READY (the default)
//   View::POS_HEAD 
//   View::POS_BEGIN
//   View::POS_END
$position = \yii\web\View::POS_END;
 
$this->registerJs($jsBlock, $position);
?>

In the controller, add an action to handle the AJAX request. Eg: In the SiteController we would add action quote (file [app]/controllers/SiteController.php):

<?php
...
class SiteController extends Controller
{
    ...
 
    // to show random quote open: index.php?r=mycontroller/quote
    // to show quote with ID 2:   index.php?r=mycontroller/quote&query=2
    // to show error:             index.php?r=mycontroller/quote&query=5
    public function actionQuote()
    {
        // some parameter from JS
        // ex. 127.0.0.1/?r=my-controller/quote&query=3
        // $queryTerm = 3;
        $queryTerm = Yii::$app->request->get('query');
        Yii::$app->response->format = 'json';
        //$quotes = ['one', 'two', 'three'];
        $quotes = [
            ['Walking on water and developing software from a specification are easy if both are frozen.', 'Edward V Berard'],
            ['It always takes longer than you expect, even when you take into account Hofstadter&rsquo;s Law.', 'Hofstadter&rsquo;s Law'],
            ['Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.', 'Rick Osborne'],
            ['I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone.', 'Bjarne Stroustrup'],
            ['Java is to JavaScript what Car is to Carpet.', 'Chris Heilmann']
        ];
        if($queryTerm !== null) { // some parameter specified
            if(isset($quotes[$queryTerm])) { // quote exists, show it
                return ['quote' => $quotes[$queryTerm]];
            } else { // quote with specified ID does not exist
                return ['error' => 'Quote with this ID does not exist.'];
            }
        } else { // no parameter - show random
            if(count($quotes) > 0) { // we got some quotes, we can random
                return ['quote' => $quotes[rand(0, count($quotes)-1)]];
            } else { // we got zero quotes, error
                return ['error' => 'Zero quotes in database. Cannot show random quote.'];
            }
        }
    }
    ...
}
?>

Source: http://www.yiiframework.com/forum/index.php/topic/54820-yii2-ajax-howto/

References:

Using PJAX (instead of AJAX)

In the view, add a Pjax block where dynamic value is going to display. Eg: For the 'Create Issue' view in [app]/views/issue/create.php:

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

In the controller, create the variables references in the view. Eg: For the 'Create Issue' view, we add this to the actionCreate() in [app]/controllers/IssueController.php:

public function actionCreate()
{
    $randomString = strtotime("now");
    $randomKey =  strtotime("now");
 
    $model = new Issue();
 
    if ($model->load(Yii::$app->request->post()) && $model->save()) {
        return $this->redirect(['view', 'id' => $model->id]);
    } else {
        return $this->render('create', [
            'model' => $model,
            'randomString' => $randomString,
            'randomKey' => $randomKey,
        ]);
    }
}    

Another example, pointing to different actions. Eg: File views\site\time-date.php

use yii\widgets\Pjax;
use yii\helpers\Html;
...
<?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(); ?>

File controllers\SiteController.php

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