⬆️ ⬇️

Grid update via ajax

Hi, habrasoobschestvo!



In this topic, I want to discuss the most appropriate use of the CGridView component in Yii.

Below I will describe 3 ways that I personally see, and I will be glad to hear your ideas in the comments.





')

So, the task :

A page with several blocks is required, one of which must have a table (grid).

Need the ability to sort and page-by-page navigation of the grid via ajax.



It sounds easy, is not it? Let's see what Yii offers us.



Method 1. Standard CRUD Generatror



The generated code contains 1 action in the controller and 1 view:

Controller:

public function actionAdmin() { $model=new Product('search'); $model->unsetAttributes(); // clear any default values if(isset($_GET['Product'])) $model->attributes=$_GET['Product']; $this->render('index',array( 'model'=>$model, )); } 


index.php :

 ... <?php $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'product-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, ... 




In general, everything works, but every time you use ajax sorting or navigation in the grid, the server returns the full html-code of the page! From which only a small html-piece is used to update the grid. If the page contains several elements (for example, one more grid), then all of them on the server side are processed for nothing. Not the most optimal solution in my opinion.



Method 2. All through Ajax



To solve the problem of method 1, create a separate action and view to form a grid. They will work only through Ajax and return the content via the renderPartial () method, which will leave only the necessary html for the grid:

Controller:

  public function actionGrid() { if(!Yii::app()->request->isAjaxRequest) throw new CHttpException('Url should be requested via ajax only'); $model=new Product('search'); $model->unsetAttributes(); // clear any default values if(isset($_GET['Product'])) $model->attributes=$_GET['Product']; $this->renderPartial('_grid',array( 'model'=>$model, )); } 


_grid.php contains only the widget code:

 ... <?php $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'product-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, ... 


and index.php pulls up the grid when the page first loads:

  <div id="grid-container"></div> yii::app()->clientScript->registerScript('load-grid', ' $("#grid-container").load("product/grid"); '); 




It looks neat, but does not work :) The renderPartial () method does not return the necessary JS and CSS files to the grid! Only Html.

But stop! RenderPartial () has an additional parameter, called “process output”, which allows you to return js and css.

Change in the controller :

  public function actionGrid() { if(!Yii::app()->request->isAjaxRequest) throw new CHttpException('Url should be requested via ajax only'); $model=new Product('search'); $model->unsetAttributes(); // clear any default values if(isset($_GET['Product'])) $model->attributes=$_GET['Product']; $this->renderPartial('_grid',array( 'model'=>$model, ), false, true); } 


We load the page and again we see bjaku: jquery.js and the rest of the scripts start loading every time we update the grid via ajax!

Of course, they should be loaded once, but renderPartial () returns them regularly with each request.

How to disable reloading scripts? To do this, you can put a separate extension , which, with ajax requests, cuts out the scripts already loaded on the page.

Voila, it works! But personally, I expect Yii to solve this problem by standard means, without extensions ...



Method 3. All through Ajax, except for the first time



And what if for the first time to load the grid with a regular request, and then update via ajax?

In the controller, everything remains almost unchanged, just remove the isAjaxRequest check and in the renderPartial without additional parameters:

  public function actionGrid() { if(!Yii::app()->request->isAjaxRequest) throw new CHttpException('Url should be requested via ajax only'); $model=new Product('search'); $model->unsetAttributes(); // clear any default values if(isset($_GET['Product'])) $model->attributes=$_GET['Product']; $this->renderPartial('_grid',array( 'model'=>$model, )); } 


In the main index.php view, we directly call the actionGrid ():

  <div id="grid-container"><?php $this->actionGrid(); ?></div> 


and in _grid.php we set the ajaxUrl parameter for future update of the grid. Otherwise, ajaxUrl will be taken from the current url (for example, product / index):

  $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'product-grid', 'dataProvider'=>$model->search() , 'filter'=>$model, 'ajaxUrl' => array('product/grid'), ... 


We look at the result: initially the grid loads correctly, but navigation does not work!

Links from pages continue to point to product / index instead of product / grid. Studying the yii code showed that the links in the pagination are not related to the ajaxUrl parameter and are taken from the current request . Which at the first boot is not the same, because we call another from one action. In my opinion, such a challenge is not even ideologically correct.

But nowhere to go, we fix it by setting the route parameter in the data provider pagination object:

_grid.php :

 <?php $dataProvider = $model->search(); $dataProvider->pagination->route = 'product/grid'; $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'product-grid', 'dataProvider'=>$dataProvider , 'filter'=>$model, 'ajaxUrl' => array($dataProvider->pagination->route), ... 


Now almost everything is as it should. Grid is loading, sorting and navigation work.

But there was a fly in the ointment:

if the grid is somehow sorted and is not on the first page, and we trigger its update via js:

 $("#product-grid").yiiGridView("update"); 


then it is reset to the first page with standard sorting. It's a shame, isn't it? This is because ajaxUrl is explicitly specified and update always sends a request for it. But if you do not specify ajaxUrl, then when updating the grid, it saves the current page and sorting ! Because inside the grid, the url for which it was obtained is stored (specifically, in the title attribute diva with keys). But it is also impossible not to specify ajaxUrl, since then requests will go to the source url of the grid, i.e. product / index.



The only solution that occurred to me is to change the very title during the first (non-ajax) grid formation. And do not specify ajaxUrl at all.

The final _grid.php looks like this:

 <?php $dataProvider = $model->search(); $dataProvider->pagination->route = 'product/grid'; $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'product-grid', 'dataProvider'=>$dataProvider , 'filter'=>$model, // 'ajaxUrl' => array($dataProvider->pagination->route), ... if(!yii::app()->request->isAjaxRequest) { yii::app()->clientScript->registerScript('grid-first-load', ' $("#product-grid").children(".keys").attr("title", "'.$this->createUrl($dataProvider->pagination->route).'"); '); } 




Now everything works as it should! Only embarrassed by the presence of the above crutches.



Conclusion



I would like to hear your ideas / comments on the methods above and on the use of grids in your yii projects.

Thanks for attention!

Source: https://habr.com/ru/post/140109/



All Articles