Loading a block through AJAX

Nowadays, it's common when part of a page is loaded asynchronously, that is, without reloading the rest of the page. Let's implement the quotes box, which will display random quotes and will have the Next quote link to show the next one.

Getting ready

  1. Create a fresh Yii application using yiic webapp as described in the official guide.
  2. Configure the application to use clean URLs.

How to do it...

Carry out the following steps:

  1. Create a new controller named protected/controllers/QuoteController.php as follows:
    <?php
    class QuoteController extends Controller
    {
       private $quotes = array(
          array('Walking on water and developing software from a specification are easy if both are frozen.', 'Edward V Berard'),
          array('It always takes longer than you expect, even when you take into account Hofstadter&rsquo;s Law.', 'Hofstadter&rsquo;s Law'),
          array('Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.', 'Rick Osborne'),
          array('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'),
          array('Java is to JavaScript what Car is to Carpet.', 'Chris Heilmann'),
       );
    
       private function getRandomQuote()
       {
          return $this->quotes[array_rand($this->quotes)];
       }
    
       function actionIndex()
       {
          $this->render('index', array(
             'quote' => $this->getRandomQuote()
           ));
       }
    
       function actionGetQuote()
       {
          $this->renderPartial('_quote', array(
             'quote' => $this->getRandomQuote(),
          ));
       }
    }
  2. We will require two views. The first is protected/views/quote/index.php:
    <h2>Quote of the day</h2>
    <div id="quote-of-the-day">
       <?php $this->renderPartial('_quote', array(
          'quote' => $quote,
       ))?>
    </div>
    <?php echo CHtml::ajaxLink('Next quote', array('getQuote'), array('update' => '#quote-of-the-day'))?>

    The second view named protected/views/quote/_quote.php is as follows:

    &ldquo;<?php echo $quote[0]?>&rdquo;, <?php echo $quote[1]?>
  3. That's it! Now, try to access your quote controller and click on the Next quote link, as shown in the following screenshot:
    How to do it...

How it works...

First, we define a list of quotes in the controller's private property, $quotes, and create a method to get a random quote. In a real application, you will probably get a quote from a database using DAO, the query builder, or an Active Record.

Then, we define a view for the index action and _quote, which is used in the getQuote action that renders it without layout and the index view as a partial. In the view action, we use CHtml::ajaxLink to create a link that makes a request to the getQuote action and updates the HTML element with the ID of quote-of-the-day. This is done with a response CHtml::ajaxLink that generates the following code in the resulting HTML page (reformatted):

<script type="text/javascript">
/*<![CDATA[*/
jQuery(function($) {
   jQuery('body').delegate('#yt0','click',function(){
      jQuery.ajax({
         'url':'/quote/getQuote',
         'cache':false,
         'success':function(html){
            jQuery("#quote-of-the-day").html(html)
         }
      });
      return false;
   });
});
/*]]>*/
</script>

As jQuery is being used, Yii includes it in the page automatically and does so only once, no matter how many times we are using it.

Note

You can see that Yii generated a #yt0 ID for us. That is great because you don't have to worry about setting IDs manually. Nevertheless, if you are loading a part of the page through AJAX and this part includes JavaScript-enabled widgets or the CHtml AJAX helpers, then you need to set IDs manually because of a possible ID intersection.

There's more...

If you want to customize the success callback, then you can do this by setting it through a third parameter as follows:

<?php echo CHtml::ajaxLink('Next quote', array('getQuote'), array('success' => new CJavaScriptExpression('function(data){
   alert(data);
}')))?>

Note

Note that we wrapped the JavaScript code with CJavaScriptExpression. This is required when you want to use JavaScript instead of a string, as in the example we just saw.

In some cases, it is not desirable to allow a non-AJAX access to the getQuote action. There are two ways that we can limit its usage to AJAX-only. First, you can use the built-in ajaxOnly filter as follows:

class QuoteController extends Controller
{
   function filters()
   {
        return array(
            'ajaxOnly + getQuote',
        );
    }
…

After adding this, a user who tries to use the getQuote action directly will get a 400 Bad Request HTTP error.

The second way is to detect if a request is made through AJAX with a special request method. For example, if we want to show the standard 404 not found page, we can do this as follows:

function actionGetQuote()
{
   if(!Yii::app()->request->isAjaxRequest)
      throw new CHttpException(404);

   $this->renderPartial('_quote', array(
      'quote' => $this->getRandomQuote(),
   ));
}

Similarly, we can use one action to serve both AJAX and non-AJAX responses:

function actionGetQuote()
{
   $quote = $this->getRandomQuote();
   if(Yii::app()->request->isAjaxRequest)
   {      
      $this->renderPartial('_quote', array(
         'quote' => $quote,
      ));
   }
   else
   {
      $this->render('index', array(
         'quote' => $quote,
      ));
   }
}

Prevent including a bundled jQuery

Sometimes, you need to suppress including a bundled jQuery, for example, if your project code uses a custom way of including JavaScript. To achieve this, you need to configure a clientScript application component using protected/config/main.php as follows:

return array(
   
   // …

   // application components
   'components'=>array(
      // …
      'clientScript' => array(
         'scriptMap' => array(
            'jquery.js'=>false,
            'jquery.min.js'=>false,
         ),
      ),
   ),

   // …
);

If you want to use your own specific version of jQuery instead, you can do so in the following way:

return array(
   
   // …

   // application components
   'components'=>array(
      // …
      'clientScript' => array(
         'scriptMap' => array(
            'jquery.js'=>'/js/jquery-1.4.2.js',
            'jquery.min.js'=>'/js/jquery-1.4.2.min.js',
         ),
      ),
   ),

   // …
);

See also

  • The Working with JSON recipe
  • The Passing configuration from PHP to JavaScript recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.6.114