Difference between #4 and #5 of Avoiding rendering entire page when using CGridView and CListView via AJAX

changed
Title
Avoiding rendering entire page when using CGridView and CListView via
AJAX
unchanged
Category
How-tos
unchanged
Tags
CGridView, AJAX, render, CListView, Interactions
changed
Content
Since I used CGridView for a first time, I didn't like how it handled operations
like sorting, filtering, changing page and etc using AJAX.  

Little background for those who are not aware of problem. When you, for example,
change page on CGridView, you send AJAX request to current action. Entire page
is rendered again just with different parameters to that particular CGridView.
After that, widget gets only CGridView part of HTML code and replaces it with
current CGridView HTML. Bad thing with this is: when entire page is rendered, it
executes all other things on page including other widgets, queries, models and
etc making it unusable for any complicated page you might have.  

You can avoid this with filters, which will be executed before controller's
action. The idea is that you extract CGridView code in separate method, so you
can invoke it from view **and** in case of AJAX request. When you call AJAX
request for specific CGridView, filter will intercept it and call method where
we have CGridView code. Clear as mud, right?  

Let's start with code, first let's create filter itself:
Create folder filters and file in it called GridViewHandler.php with following
code:


~~~
[php]
<?php
class GridViewHandler extends CFilter {

	protected function preFilter($filterChain){
		if (Yii::app()->request->getIsAjaxRequest() &&
isset($_GET["ajax"])) {
			$selectedTable = $_GET["ajax"];
			$method='_getGridView'.$selectedTable;
			if(method_exists($filterChain->controller,$method)){
				$filterChain->controller->$method();
				Yii::app()->end();
			}else{
				throw new CHttpException(400,"CGridView handler function {$method} not
defined in controller ".get_class($filterChain->controller));
			}
        }
        return true;
    }
}
?>
~~~

In controller where you have CGridView (famous Post controller), add filter
(**after** accessControl if you have it), and extract CGridView code in separate
method like this:

~~~
[php]
<?php
class PostController extends Controller{

	public function filters(){
		return array(
			array(
				'application.filters.GridViewHandler' //path to GridViewHandler.php class
			)
		);
	}

	public function actionIndex(){
		//you call _getGridViewPost in index view to display CGridView
		$this->render('index');	
	}

	//This is called by filter when CGridView (with id="Post") is invoked
with AJAX request
	public function _getGridViewPost(){ 
		//create data provider and renderPartial CGridView widget
	}
}
~~~

So in index view, put _$this->_getGridViewPost()_ in place where you want
your CGridView. In _ _getGridViewPost_ method, create your dataProvider and call
_$this->renderPartial()_ of view where you have your CGridView widget.

That's it! On first request, everything works as normal but when you interact
with CGridView, filter intercepts request and call only method that is related
to CGridView. This also satisfies **Convention Over Confituration** thing.
Method with CGridView must have name _getGridView{$ajaxVar} where {$ajaxVar} is
widget ID (which is passed by $_GET['ajax']).

This thing also works for CListViewAlso, in very
similar way.theory, this should work in CListView too but I didn't
try it to be honest :)