CListView AJAX filtering

  1. String filter
  2. Extended use

This tutorial shows how to filter CListView items by AJAX, and it's compatible with disabled JavaScript users
In my case this has been done to filter users list

String filter

View

In our view file we have something like this

$this->widget('zii.widgets.CListView', array(
	'dataProvider'=>$dataProvider,
	'itemView'=>'viewList',
    'sortableAttributes'=>array(
        'id'=>'cronologico',
        'transaction'
    ),
    'id'=>'ajaxListView',
));

We now add a form with a text field (remember to change form action), the value of the field get updated with the GET parameter

echo CHtml::beginForm(CHtml::normalizeUrl(array('message/index')), 'get', array('id'=>'filter-form'))
    . CHtml::textField('string', (isset($_GET['string'])) ? $_GET['string'] : '', array('id'=>'string'))
    . CHtml::submitButton('Search', array('name'=>''))
    . CHtml::endForm();

and a JQuery script that sends AJAX request after a short delay (this reduces DB queries)

Yii::app()->clientScript->registerScript('search',
    "var ajaxUpdateTimeout;
    var ajaxRequest;
    $('input#string').keyup(function(){
        ajaxRequest = $(this).serialize();
        clearTimeout(ajaxUpdateTimeout);
        ajaxUpdateTimeout = setTimeout(function () {
            $.fn.yiiListView.update(
// this is the id of the CListView
                'ajaxListView',
                {data: ajaxRequest}
            )
        },
// this is the delay
        300);
    });"
);
Controller action

This is the controller action

public function actionIndex( $string = '' )
{
	$criteria = new CDbCriteria();
	if( strlen( $string ) > 0 )
		$criteria->addSearchCondition( 'username', $string, true, 'OR' );
	$dataProvider = new CActiveDataProvider( 'User', array( 'criteria' => $criteria, ) );
	$this->render( 'index', array( 'dataProvider' => $dataProvider ) );
}

Extended use

I had to implement a more sophisticated filter for my offer CListView: selecting checkBoxes with integer value the search had to be filtered with an IN condition.
CheckBoxes get selected by the GET parameter

View
echo CHtml::checkBoxList('category', (isset($_GET['category'])) ? $_GET['category'] : '', $category[$key],
    array(
        'class'=>'categoryFilter',
        )
    );

Then i had to add this javaScript for the AJAX filtering

[javascript]
$('.categoryFilter').change(function(){
    category = $('.categoryFilter').serialize();
    $.fn.yiiListView.update(
        'ajaxListView',
        {data: category}
    );
});
Controller

CheckBoxes GET parameters are variable (in my case up to 21) so I had to use an array in my controller.

public function actionIndex( $string = '', array $category = array() )
{
	$criteria = new CDbCriteria();

	if( strlen( $string ) > 0 ) {
		$criteria->addSearchCondition( 'title', $string, true, 'OR' );
		$criteria->addSearchCondition( 'description', $string, true, 'OR' );
	}
	if( count( $category ) > 0 )
		$criteria->addInCondition( 'category', $category );
	$dataProvider = new CActiveDataProvider( 'Offer', array( 'criteria' => $criteria, ) );
	$this->render( 'index', array( 'dataProvider' => $dataProvider ) );
}

May be usefull to add buttons that selects and deselects all checkBoxes

echo CHtml::button('All', array('id'=>'selectAll'))
. CHtml::button('None', array('id'=>'selectNone'));

And register the following JavaScript ~~~ [javascript]

$('#selectAll').click(function(){
    $('.categoryFilter').attr('checked','checked')
    category = $('.categoryFilter').serialize();
    $.fn.yiiListView.update(
        'ajaxListView',
        {data: category}
    );
});
$('#selectNone').click(function(){
    $('.categoryFilter').removeAttr('checked')
    category = $('.categoryFilter').serialize();
    $.fn.yiiListView.update(
        'ajaxListView',
        {data: category}
    );
});