Cgridview Doesn't Filter Inside Form

I’m using CGridView inside a form. It has selectable rows that are recorded in the database when the form is submitted. It works fine but we decided we needed to use the filtering capability of CGridView to limit the number of rows. I set the widget up to use the filter but it didn’t work.

After playing around with it I found that if the CGridView widget is NOT located inside the form then the filter works as expected. If it’s embedded in a form then the filter fields at the top are there, but they don’t do anything.

Is this a bug? Is there a work-around?

If anyone knows anything about why CGridView apparently doesn’t filter when it’s nested inside a form I’d sure like to know your experience. I assume it is a bug and will hopefully be fixed in Yii 2.0.

It would seem my only recourse is to code a custom form based filter and use the user’s input data to to construct the filtering criteria to pass to CActiveDataProvider.

Hi tommy,

I never had a problem like that with a CGridView inside a form.

Though it’s only a guess, but you might be mixing up “$model” for the form and “$model” for the CGridView filter.

Could you share your code for the controller and the view?

Are you using GET or POST for your form?

This is in the view:




    <?php

        echo CHtml::beginForm();


        $criteria = new CDbCriteria;

        $criteria->compare('recipient', $modelc->recipient, true);

        $criteria->addCondition("id not in (select calleeId from xref where callgroupId = $model->id)");

        $dataProvider=new CActiveDataProvider('Callee', array('criteria'=>$criteria));


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

            'dataProvider'=>$dataProvider,

            'filter'=>$modelc,

            'selectableRows' => 22,

            'columns' => array(

                array(

                    'id' => 'selectedIds',

                    'class' => 'CCheckBoxColumn'

                ), 

                'recipient',

            ),

        ));

    ?>

    <div>

        <input type="hidden" name="cgid" value="<?php echo "$model->id"; ?>">	

        <?php echo CHtml::submitButton('Assign', array('name' => 'AddButton'));

        ?>

    </div> 


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



And this is in the controller:




            $modelc=new Callee('search');

            $modelc->unsetAttributes();  // clear any default values

            if(isset($_GET['Callee'])) $modelc->attributes=$_GET['Callee'];


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

                'modelc'=>$modelc,

                'model'=>$this->loadModel($id),

            )); 



The weird thing is all I have to do is comment out the two lines: "echo CHtml::beginForm();" and "echo CHtml::endForm();" and then the filter works fine!

I’m using “CHtml::beginForm();” and “CHtml::endForm();”. This renders the form as method=“post”.

I see. So you are using $modelc of Callee for the filter and $model for the form. I think it’s OK.

Bu how do you handle the post of the form in your controller?

Here’s the complete function in the controller:




       public function actionAssign($id)

        {

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

            {

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

                {

                    foreach ($_POST['selectedIds'] as $delid)

                    {

                        $both = explode(",", $delid);

                        Xref::model()->deleteAll("callgroupId=$both[1] and calleeId=$both[0]");

                    }

                }

            }

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

            {

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

                {

                    foreach ($_POST['selectedIds'] as $addid)

                    {

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

                            $xref = new Xref;

                            $xref->calleeId = $addid;

                            $xref->callgroupId = $_POST['cgid'];

                            $xref->save();

                        }

                    }

                }

            }

            $modelc=new Callee('search');

            $modelc->unsetAttributes();  // clear any default values

            if(isset($_GET['Callee'])) $modelc->attributes=$_GET['Callee'];


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

                'modelc'=>$modelc,

                'model'=>$this->loadModel($id),

            )); 

        }




What it does is allow the user to assign (or de-assign) Callees to Callgroups. They can check the boxes individually or they can select the “all” checkbox at the top of the grid which automatically checks all the rows. There is a many-to-many relationship between the Callee model and the Callgroup model which is resolved with the Xref model Everything works beautifully, except when I add the filter. I tried replacing the CHtml stuff with standard <form> </form> and <input type=“submit”…> tags. It worked as before - it applied the assignments correctly but filtering didn’t respond. But if I just remove all the form tags the filter works!

OK, now I think I could reproduce the problem.

The cause of the problem is that when you hit enter key in a filter field of the CGridView, then the form get an implicit submisstion.

Just for testing purpose, use tab key instead of enter key in a filter field, then the grid view will be correctly updated without a submission of the form.

Probably you can suppress the implicit submission of the form by replacing the "submit" type inputs to "button" type ones. That means, using CHtml::button() instead of CHtml::submitButton().

Softark thank you so much! You got it exactly right. So there is not a bug in Cgridview, it’s just that by default a form gets submitted when you press enter inside a form field. I hadn’t realized that the filter field is treated like any other form field in that respect.

After looking around a little I found some javascript that disables the "enter causes submit" aspect of a form:




<script type="text/javascript">

$(window).keydown(function(event){

    if((event.which== 13) && ($(event.target)[0]!=$("textarea")[0])) {

      event.preventDefault();

      return false;

    }

  });

</script>



So now the user can press "enter" after typing in filter criteria and the filtered result will be displayed, and they must actually click on "Assign" to process the assignment.

Thanks again Softark. You are a rock star in my book!