Friday, October 14, 2011

Updating a Listview in Yii with Ajax

This was an unbelievable exasperating feature to add. I have no idea why this didn't work in the myriad ways I tried to make it work, but I've got it working, now, so I wanted to post.

I looked at every example of updating a CListview that I could find on Google. None of them were doing what I wanted to do (and what I'd assumed was a very common task). In a nutshell, I wanted to have a listview on my view page and then update the results when someone clicked on a link. All of the examples I saw involved submitting a form. I saw one that was using a link, but the details provided in how they solved it were so miniscule as to be almost humorous (had I not been so frustrated).

I won't discuss this solution in great detail. I'll simply include the information below.

The View
At the top of the index.php view, I have this:

<?php

Yii::app()->clientScript->registerScript('ajaxUpdate',
"
$('.ajax_link').click(function(){
$.fn.yiiListView.update('companyList')
});
return false;
", CClientScript::POS_READY);
?>

Inside the view itself is the listview:


<?php $this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_view',
'id'=>'companyList',
)); ?>

The links are generated in the view, too, of course. They look like this:


foreach($allLetters as $letter) {
if(in_array($letter, $dbInitials)) {
/*
echo CHtml::ajaxLink(strtoupper($letter), CHtml::normalizeUrl(array('index', 'initial'=>$letter)),
array('success'=>"$.fn.yiiListView.update('companyList')")
);
*/
echo CHtml::link(strtoupper($letter), CHtml::normalizeUrl(array('index','initial'=>$letter)), array(
'class'=>'ajax_link',
));
} else {
echo strtoupper($letter);
}
// Add some whitespace.
echo '&nbsp;&nbsp;';

}

In fact, that's pretty much all there is in my index.php page. A Javascript portion that is registered as a client script, a list view with the id set to companyList so I can refer to it later, and a series of basic html links with a class of ajax_link so that the click event is triggered in the Javascript.

On the server side, the controller action for index looks like this:

public function actionIndex($initial='')
{
if($initial) {
$criteria = new CDbCriteria;
$criteria->compare('name', $initial . '%', true, 'AND', false);
} else {
// Retrieve all companies to list
$criteria = new CDbCriteria;
$criteria->with=array('video','image','state');
$criteria->together = TRUE;
}
// Only admin users can see ALL records. Others can only see approved records.
if(!Yii::app()->user->checkAccess('admin')) {
$criteria->addCondition('t.isApproved=:isApproved');
$criteria->params[':isApproved']=1;
}
$dataProvider=new CActiveDataProvider('Company', array('criteria'=>$criteria));

$this->render('index',array(
'dataProvider'=>$dataProvider,
'dbInitials'=>$this->dbInitials,
'allLetters'=>$this->allLetters,
));
}

If no parameters are submitted, it just renders all of the results. If an initial letter is submitted, it filters the results. Either way, the index view is rendered.

The main problems I encountered were that:
  1. The AJAX request would return the results I wanted, but the div wouldn't update.
  2. Once I finally got the div to update, I was getting an error from the yiiListView.update() method. Adding the "return false" in the Javascript code fixed that.
Done.

3 comments:

  1. hi having the same problems with u but tried your soln it still returns

    "url is undefined" when i attach the $.fn.yiiListView.update('companyList')

    any encounters with it before?

    ReplyDelete
  2. Thanks, I'm trying to do something similar with my Lucene search results. It's helpful to see how someone else went about it.

    By the way, I'm also a UCSD graduate (2009). I was pretty excited to come across the blog of a female web developer who uses Yii and went to my school! :)

    ReplyDelete
  3. For Yii newbs coming across this, you should be aware that CListView comes with ajax sorting and paginating built in...

    Check out http://www.yiiframework.com/doc/api/1.1/CListView

    and set enableSorting to true

    ReplyDelete