Select box on CGridView and CListView

Is there a way to put a select box on every row of the items on grid list view for example to delete selected records.

I already tried setting a public variable on my model like $_select and on my CGridView settings




...

'columns'=>array(

  array(

    'header'=>'',

    'name'=>'_select',

    'value'=>CHtml::checkBox('selectedItems', false, array('value'=>$model->id)),

  ),

...



Another issue. I know I’m doing something wrong. -like always! :(

I’ve created this files and I want ajax delete selected itens but I don’t get it working!!! What am I doing wrong? :(

controllers/GalleriesController.php




...

        public function actionView()

	{

		$model=$this->loadModel();

		$dataProvider=new CActiveDataProvider('GalleriesImages', array(

			'criteria'=>array(

				'condition'=>'galleryId=' . $model->id,

			),

			'pagination'=>array(

				'pageSize'=>30,

			),

		));

		$this->render('view',array(

			'model'=>$model,

			'dataProvider'=>$dataProvider,

		));

		

	}

...



views/galleries/view.php




...

<div class="form">

<?php echo CHtml::beginForm(); ?>

<?php $this->widget('zii.widgets.CListView', array(

	'dataProvider'=>$dataProvider,

	'itemView'=>'_viewImages',

	'template'=>"{pager}\n{sorter}\n{items}\n{summary}\n{pager}",

	'sortableAttributes'=>array(

		'caption',

		'description',

		'dateCreation',

	),

)); ?>

<div class="row buttons">

<?php echo CHtml::ajaxSubmitButton('Delete Selected', CHtml::normalizeUrl(array('galleriesimages/delete'))); ?>

</div>

<?php echo CHtml::endForm(); ?>

...



views/galleries/_viewImages.php




...

<?php

$model=$this->loadModel();

$directoryUrl=CC::getMediaPath(true) . $model->title . '/';

?>

<?php echo CHtml::checkBox('selectedImages', false, array('value'=>$data->id) ); ?>&nbsp;

<a rel="gallery" href="<?php echo $directoryUrl . CHtml::encode($data->filename); ?>" target="_blank">

	<img src="<?php echo $directoryUrl . CHtml::encode($data->filenameThumbnail);?>" alt="<?php echo CHtml::encode($data->caption);?> " />

</a>

...



controllers/GalleriesImagesController.php




...

	public function actionDelete()

	{

		if(Yii::app()->request->isPostRequest)

		{

			if(!Yii::app()->request->isAjaxRequest)

			{

				$this->loadModel()->delete();

			} else {

				foreach($_POST['selectedImages'] as $i)

				{

					$model=GalleriesImages::model()->findbyPk($i);

					$model->delete();

					Yii::app()->end();

				}

			}

			if(!isset($_GET['ajax']))

				$this->redirect($_SERVER['HTTP_REFERER']);

		} else {

			throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');

		}

	}

...



Thanks for help!

Hi thiagovidal!!

You can use CCeckBoxColumn like that:




		array(

			'class'=>'CCheckBoxColumn',

			'id'=>'rows_to_delete'

		),



This checkbox will submit at an array in $_POST[‘rows_to_delete’] with the primaryKey of the selected rows.

You have to include in a form the datagrid and to put a submitbutton (or ajaxSubmitButton).

Remember that if you have to submit many data in post (like many checkbox) you should always put the id in the name of the checkbox, in order to recognise wich checkbox is selected. So for example:




...

'columns'=>array(

  array(

    'header'=>'',

    'name'=>'_select',

    'value'=>CHtml::checkBox('selectedItems[$data->primaryKey]', false, array('value'=>$model->id)),

  ),

...



Or something like that. For your case I think is better to use CCheckBoxColum.

Another option is to use CGriedViews selection feature. It’s activated by default. You can add a little javascript to get the current selection any time (e.g. when a “delete all” button is clicked). Something like this should work:


$deleteUrl=$this->createUrl('galleries/delete');

Yii::app()->clientScript->registerScript('initGridEvents',<<<EOD


    $('#deletebutton').click(function() {

        var selectionIds=$.fn.yiiGridView.getSelection('gridviewId');


        if (selectionIds.length!==0) {

            $.ajax.({

                type: 'POST',

                url: '$deleteUrl',

                data: {ids: selectionIds},

                dataType: 'json',

                success: function() {

                    $.fn.yiiGridView.updateGrid('gridviewId');

                }

            });

        }

    });

EOD

,CClientScript::POS_READY);



In your action you get the Ids to delete from $_GET[‘ids’]. You can return some JSON object with success/error informantion and check that response in your success handler on the client side.

Thank a lot guys. I tested both suggestions and both worked like a charm. Thanks again! Its wonderful when you need receive help when u got stuck.

But… there’s something new=>

Ok. The chekbox now exists… but the grid view don’t let me select more than one line per time.

I put this code on my controller to handle the ajax request. Is this right?





	public function actionDelete()

	{

		if(Yii::app()->request->isPostRequest)

		{

			if(isset($_POST['selectedItems']))

			{

				foreach($_POST['selectedItems']['id'] as $i)

				{

					$model=Users::model()->findbyPk($i);

					$model->delete();

				}

			} else {

				$this->loadModel()->delete();

			}


			if(!isset($_GET['ajax']))

				$this->redirect(array('admin'));

		}

		else

			throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');

	}



Almost there… Solved this part.

Included on the settings of CGridView [size=“3”]‘selectableRows’=>2,[/size].




<?php

$this->widget('zii.widgets.grid.CGridView', array(

	'id'=>'users-grid',

	'dataProvider'=>$model->search(),

	'filter'=>$model,

	'selectableRows'=>2,

	'columns'=>array(

		array(

			'class'=>'CCheckBoxColumn',

			'id'=>'selectedItems'

		),

		array(

			'header'=>'Actions',

			'class'=>'CButtonColumn',

		),

		'username',

		'firstName',

		'lastName',

		'birthday',

		'email',

		'dateCreation',

		'dateLastVisit',

		'status',

		'role',

	),

));

?>




It doesn’t work. Can anyone help me?

You said above, you tested both methods and both worked. So what did you change?

Yes. On the both tests I could select the rows to delete but the jquery doesn’t work. And I can’t handle the data on my controller. I can’t get the ids with foreach.

I think you need to serialize it like this. I got it working…




    array(

      'header'=>'',

      'name'=>'_select',

      'value'=>'CHtml::checkBox("selectedItems[$data->Id]", false, array("value"=>$data->Id, "class"=>"deleteMe"))',

      'type'=>'raw',

    ),




$('#deleteButton').click(function() {

  //var selectionIds=$.fn.yiiGridView.getSelection('profileGrid');

  var data=$('input.deleteMe').serialize();

  if (data.length!==0) {

    $.ajax({

      type: 'POST',

      url: '$deleteUrl',

      data: data,

      dataType: 'json',

      success: function() {

        $.fn.yiiGridView.update('profileGrid');

      }

    });

  }

})";



But to have a "select all" checkbox at the header?

any reason why the grid is not updated after success?

View




/*grid*/

    array(

      'header'=>'',

      'name'=>'_select',

      'value'=>'CHtml::checkBox("selectedItems[$data->Id]", false, array("value"=>$data->Id,    "class"=>"deleteMe"))',

      'type'=>'raw',

),





$deleteUrl=$this->createUrl('product/deleteAll');

$js = "

$('#deleteButton').click(function() {

  var data=$('input.deleteMe').serialize();

  if (data.length!==0) {

    $.ajax({

      type: 'POST',

      url: '$deleteUrl',

      data: data,

      dataType: 'json',

      success: function() {

        alert(\"success..updating grid\"); <---not popping up..

        $.fn.yiiGridView.update('#profileGrid');

      }

    });

})";

Yii::app()->clientScript->registerScript('initGridEvents',$js,CClientScript::POS_READY);



Controller





  public function actionDeleteAll()

  {

    

    if(Yii::app()->request->isAjaxRequest)

    {     

      if(isset($_POST['selectedItems']))

      {

        $ids = $_POST['selectedItems'];     

        foreach ($ids as $key => $value) {

           $model = Product::model()->findByPK($key);

          $model->delete();

       } 

    }

    

     Yii::app()->end();

   }

   else

     throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');

  }



I was having problems doing something like this earlier today, when I noticed that $.fn.yiiGridView.getSelection() did not alwas give me back the ids of the rows that were checked. If this is the problem you are having, then I can help.

I figured out this was a problem with the way the widget handles css class states, and fixed it. I added a new extension you can download here:

http://www.yiiframework.com/extension/fixed-checkbox-column/

It is a direct replacement for CCheckBoxColumn, so once you download it, you only have to change the column class name from CCheckBoxColumn to FixedCheckBoxColumn

I hope this helps.

Corey

@MadSkillsTisdale:

I think it makes much more sense to create a bug report at google than creating a bug fix extension. Qiang is pretty fast in fixing bugs. Even more, if you supply a solution. ;)

any reason why the grid is not updated after success? #10

and

But how to add a "select all" checkbox at the header? I did code below but get this error:

syntax error

toggleAllCheckBox(selectedItems[]);

How do I pass array[] into javascript?




    array(

      'header'=>'<input type="checkbox" value="" onclick="toggleAllCheckBox(selectedItems[]);" />',

      'name'=>'_select',

      'value'=>'CHtml::checkBox("selectedItems[$data->Id]", false, array("value"=>$data->Id, "class"=>"deleteMe"))',

      'type'=>'raw',

    ),


<script type="text/javascript">

checkFlag = false;

function toggleAllCheckBox(box) {

  checkFlag = !checkFlag;

  var checked_count = 0;

  

  if (!box) return;

  

  if(box.length) {

    for(var i = 0; i < box.length; i++) {

      box[i].checked = checkFlag;

      if (checkFlag)

        checked_count++;

    }

  } else {

    box.checked = checkFlag

    if (checkFlag)

      checked_count++;

  }

  

  document.adminForm.boxchecked.value = checked_count;

   

}

</script>



I am trying this




    array( 

      'name'=>'', 

      'value'=>'CHtml::checkBoxList("selectedItems",null,array("value"=>$data->Id),array("checkAll"=>"Check/Uncheck All"))',

      'type'=>'raw',

    ),



But its giving me this :

I spent the whole day trying to get this to work, and almost gave up. <_<

But then I got the clever idea of just doing this:


<?php echo CHtml::ajaxLink("Show Selected",

 $this->createUrl('reqTest03'),

 array("type" => "post",

 "data" => "js:{ids:$.fn.yiiGridView.getSelection('issue-grid')}",

"update" => "#output")); ?>



In my controller:


	public function actionReqTest03() {

    	if(Yii::app()->request->isAjaxRequest){

        	if(isset($_POST['ids'])) {

            	foreach($_POST['ids'] as $val) {

                	echo $val . '<br/>';

            	}

        	}

    	}

	}



In the gridview:


<?php $this->widget('zii.widgets.grid.CGridView', array(

	'id'=>'issue-grid',

	'dataProvider'=>$model->search(),

	'filter'=>$model,

    	'selectableRows'=>2,

    	'pager' => array('class' => 'CustomLinkPager'),

    	'columns'=>array(

            	array(

                	'class'=>'CCheckBoxColumn',

                	'id'=>'selectedItems'

            	),

...



Just wanted to add that here - maybe someone will find it useful.

The only problem now is that it simply fails if there is no selection.

How can I improve this line? ->


        	"data" => "js:{ids:$.fn.yiiGridView.getSelection('issue-grid')}",



If it’s empty, it fails, and nothing is submitted.

I think I need to study some jQuery, maybe.

Any tips ?

Can you tell more about how “it simply fails”? What’s happening? E.g. what’s print_r($_POST) in your controller action? You do use firebug or something to observe all ajax requests and their GET and POST data, do you?

I inserted this in the action:


die('Horrible Death');

and it wasn’t called when there was no selection.

It’s not a problem, though.

It’s actually a good thing that it only fires when fed valid data.

'was only a problem when I was debugging because then the output div wasn’t updated.

Once I put it in real-world use, it didn’t matter.

But thanks for the hint about firebug. ;)

Hi zaccaria (and others),

I have a multi-page form and in the first two pages each, I have a CGridView widget that uses a CCheckBoxColumn (as described above) surrounded by an CActiveForm and a submit button. Both these forms have ‘stateful’ set to true.

The filter and sort functions of the grid in the first page works correctly. After selecting the required row and submitting, the user arrives at the second page. Now, in this second grid, when I enter a filter value or try to sort any of the columns, the grid completely disappears.

The second grid/page works fine by itself, and it seems to disappear only if it follows another form with a grid. I tried debugging using Firebug but am unable to (quite new to PHP and Firebug). Would really appreciate any help.

I figured out the problem (though not the solution). Only one action method (create) is invoked and this renders the multiple pages/forms. I followed this example in the first answer of this post.

The first form is associated with a particular model and the grid displays rows of this model, and this is the page called by default on this action. The subsequent form is rendered inside the same create action on successful post & submit on the first form, and the grid in this second form is associated with a different model. Now, the filter and sort functions for this second grid seems to call the URL for the create action with a parameter and this call invokes the default first form’s functions, which is incorrect.

Basically, I want the user to be able to search using gridview and choose the required field in the first two pages/forms within the same action. Can you please provide pointers on how to tackle this issue? Thank you.

Edit: I think the problem is similar to the one explained in this post, but please advise the best way to handle this.