Ascendro Blog

Displaying 1-1 of 1 result.

Handling multiple CGridview's via AJAX

A. Requirement

Multiple CGridViews are embedded in the same page and we need to load some of them via AJAX by clicking on the primary grid item.

B. Initial solution

We need to use renderPartial method for showing the secondary grid via AJAX and replace some DOM selector with the AJAX response.

C. Challenge

By clicking multiple times on same items in the primary grid will cause the secondary (details) grid to make multiple sorting AJAX requests. This is due to the multiple bindings for jQuery grid init code on the same grid selector by calling renderPartial method with processOutput parameter allways set to TRUE.

D. Final solution

On renderPartial we should set the processOutput param to TRUE only first time when the grid was requested via AJAX (basically when user first clicks on the item in the primary grid), otherwise processOutput should be FALSE on AJAX requests;

For this we need to know how many times the user clicks on each primary grid item and set the processOutput to TRUE only on first click. We will count the clicks using DOM local storage in order to track clicks between primary grid filter/pagination/sorting requests.

Will also add the processOutput parameter to the grid AJAX requests to control the rendering behavior of the page in controller. In the page context, on the first AJAX request for specific grid the processOutput will be active(1), after first request this parameter will be disabled.

1. Controller : add ajaxProcessOutput flag and set this flag in beforeAction method, AJAX render action will bind this parameter on renderPartial

    /**
     * @var bool flag to determine we should process output of AJAX actions
     * this should be used as "processOutput" parameter for renderPartial method of AJAX actions
     * the idea is that renderPartial should process output just the first time when AJAX action was made to avoid multiple requests on grid views
     */    
    public $ajaxProcessOutput = false;
     
    .....

    public function beforeAction($action) {
            .... 
            $this->_setAjaxProcessOutput();
        }
        return parent::beforeAction($action);
    }

    /**
    * set ajaxProcessOutput flag and then remove the request parameter to not affect paging and sorting of grids rendered by ajax
    */  
    private function _setAjaxProcessOutput(){
        if(Yii::app()->request->isAjaxRequest && !empty($_REQUEST['processOutput'])){
            $this->ajaxProcessOutput = true;
            if(isset($_GET['processOutput'])){
                unset($_GET['processOutput']);
            }
            if(isset($_POST['processOutput'])){
                unset($_POST['processOutput']);
            }
            unset($_REQUEST['processOutput']);
        }
    }

   /**
   * Sample AJAX action for displaying primary entity details as grid
   */  
   public function actionDetails($id = null)
    {
       
        if ( $id !== null ) {
            $id = (int) $id;
            $model = new DetailModel('search');
            $model->userID = $id;   
            if ( isset($_GET['DetailModel']) ) {
                $model->attributes = $_GET['DetailModel'];
            }
            $this->renderPartial('_detailsGrid', array('model' => $model), false, $this->ajaxProcessOutput);
        }
    }

2. View: details grid view loaded via AJAX

$this->widget('zii.widgets.grid.CGridView', array(
    'id'=>'user-documents-grid',
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    'columns'=>array(
    'id',
    'name',
    'created'
));

3. JS: load details grid via AJAX, the processOutput request parameter is computed based on number of clicks on the primary grid item

$(document).ready(function () {
    /* List user documents list via AJAX grid */
    $(document).on('click', '.user-item-selector', function() {
        var $element = $(this);
        var userID = $(this).parent('tr').data('row-id');
    // track item clicks and set the AJAX request processOutput parameter
        var processAjaxOutput = ajaxGrid.setRequestProcessOutput( '.part-with-details', itemID );
        $.get(
            '/entityController/details',
            { id: userID, processOutput : processAjaxOutput },
            function(result) {
                $('.part-with-details').html(result);
            }
        );
        return false;
    });
});
.....
var ajaxGrid = {
    // count grid item clicks; will use grid wrapper to store data because the parent grid can have pagination, and we must keep track of clicks between pages
    countItemClicks : function( containerSelector, itemID  ){
        var container = $(containerSelector);
        var dataKey = 'clicks_' + itemID;
        var clicks =  container.data(dataKey);
        // set initial value or update the counter
        if(typeof clicks === 'undefined'){
            clicks = 1;
        }else{
            clicks = clicks + 1;
        }
        // store updated data on the container node
        container.data(dataKey, clicks);
        return clicks;

    },
    setRequestProcessOutput: function( containerSelector, itemID  ){
        var itemClicks = ajaxGrid.countItemClicks( containerSelector, itemID );
        if(itemClicks > 1){
            return 0;
        }
        return 1;
    }
};

E. Conclusion

This solution can be used in the context in which we have several grids on the same web page and some of them are always rendered by AJAX.

LEAVE A COMMENT

No results found.