Modifying CGridView's default filter/search behaviour

Hi there,

Two quick questions:

  1. Is there a way to remove or disable only one or few but not all filter input boxex in CGridView’s header? I’ve managed to block searching/filtering of unwanted columns by modifying model’s rules and search methods and removing unwanted fields from _search.php view. So corresponding filter inputs are unusable, but still visible. Anyway to fix this?

BTW: Should it be done automatically? I.e. if GridView widget has access to model, can’t it check which fields are set on for searching (in rules) and render filter input boxes only for them?

  1. What is the best way to implement reset button to search/filter form that will not only clear the form but also remove anything from filter input boxes and also force GridView’s update?

I tried adding simple reset button:


<?php echo CHtml::resetButton('Clear!', array('id'=>'form-reset-button')); ?>

and some jQuery code to main view file - I thought that updating GridView with empty set of data will do the trick:


$('#form-reset-button').click(function()

{

            	$.fn.yiiGridView.update('zlecenia-grid', {});

            	return false;

});

But this seems to be not working.

For the first problem… you missed the filter propery of CDataColumn - http://www.yiiframework.com/doc/api/1.1/CDataColumn#filter-detail

just set it to ‘false’ for the columns you don’t need a filter

Thanks! I’m surely missed it, because it seems so obvious… But sometimes those misses are good as in this case, because I manage to now that with little effort only this filter can be changed to a combox. Brilliant! :]

And as for second problem, any clue? I was able to figure out that repeating submit code in Reset button click function:


$.fn.yiiGridView.update('zlecenia-grid', {data: $(this).serialize()});

(which, as I thought, should sent to gridview an empty set of params and therefore reset the whole grid) does not work. Mainly because $(this).serialize() is empty in this case and I don’t have closer idea how to recreate, what submit is sending (params array), only with empty (zeroed) params values.

And, if we are talking about refreshing list, has any got any idea about my third problem (described here - filtering CListView like CGridView)? I don’t like reminding myself, but this is rather hot problem for me right now and even I thought it will be ease (or at least should be - as both classes are descent of the same parent class), it seems that after twelve hours of thinking over it I found nothing! :[

For the reset you need to send all filter input parameters set to empty…

Yes, I was aware of this. But how to do this? If sending:


$.fn.yiiGridView.update('zlecenia-grid', {data: $(this).serialize()});

in reset button click is not enough, because in this situation $(this).serialize() is empty, and surprisingly this (feeding filters with an empty array) does not reset them, then the only way I see is to iterate through all form elements, get with JS their name, build array containing those names and empty values and then send it to GridView. Am I right?

Seems a hard work and before I’ll do this I wanted to make myself sure that there isn’t a quicker, easier, better way?

BTW: Why feeding gridview with empty data set does not reset filters and gridview itself (as should be doing, if I’m not mistaken)? Maybe I need to only update/fix JS/jQ code responsible for catching update method in gridview, to solve this?

You have to check the jquery.yiigridview.js

the $.fn.yiiGridView.update function line 146… -


options.url = $.param.querystring(options.url, options.data);

Here in options.url we get the filter values used to render the current "filtered page" (current filter like "name=ar&id=2" )

options.data is the current state of the filter inputs obtainde at the line 44/45 -


var data = $.param($(inputSelector));

$.fn.yiiGridView.update(id, {data: data});

$.param.querystring() merges the two sets… so if you just send an empty "options.data"… the current filter will remain… as per current implementation you need to send all filter inputs as empty so that the previous one gets replaced…

Am I missing something or if using filter as combo box is not working?

I have a column where currently I got ‘WLWD’ for every record. I declared filter for that column to be a combo box, having:


'filter'=>array('test', 'test2', 'test3', 'WLWD'),

Combo box filter is drawn correctly, but whenever I select any of first three options, I see whole set of data while for my reason I should see none - i.e. there is no record in current dataset, which has value in that column equal to test, test1 or test2 respectively. What am I missing?

Problem 2: It seems hard for me to believe that whoever created so wonderfully working GridView hasn’t thought about resetting it directly from search form connected to it and made resetting GridView to inital state easy. Maybe it is worth opening ticket to request such feature?

Yes! You’ve already explained me this. And I’m looking for own piece of code that will do this. I’m only wondering, why code like this:


$.fn.yiiGridView.update('zlecenia-grid', {data: $(this).serialize()});

works fine in submit method - i.e. we don’t have to iterate through all form fields and build set of data being sent to GridView’s update manually? And why the same approach does not work in reset click method, meaning that why serializing there the same object does return an empty set instead of all filters names with empty values? Which is - as you explained - required by GridView to reset itself to an initial state?

Maybe my misunderstanding comes from that right now I’m looking at this problem more like theoretical not coding problem.

I.e. We have a form with fields. Fields has values. When clicking submit we do not iterate all fields, instead we’re just serializing parent object (form?) and all works like a charm. All filters data is properly formatted to an array form and sent to gridview, which properly processes it.

Then we take reset button, we’re doing exactly the same with only difference that before serializing object, we resetting (clearing) all form fields. If we are doing exactly the same, the same result should be received, i.e. we should receive array containing fields name and their values, which are empty in this situation. Instead we are getting empty set and I have no bloody idea, why?

The only reason I may think of - completely wako - is that $(this).serialize() implementation differs between submit and click functions or that $(this) refers to a different object in both cases. Both ideas are hard to believe in.

Problem 1 - the filtering works just fine… it sets the chosen value to an attribute… you need to check the $model->search() to see what you are doing with that attribute and what condition are you making with it…

Problem 2 - your last line says it all… $(this) is always the current object… but you need the filter input fields… in jquery.yiigridview.js that is the variable "inputSelector" (line 42)

Damn I’m stupid! :confused: Thats all I can say in response…

BTW: what is exact difference between changing model’s search() function and line:


//The following rule is used by search().

//Please remove those attributes that should not be searched.

array('DATR, BCODE, PATIENT, PESEL, UDATE', 'safe', 'on'=>'search'),

in model’s rules() function?

Both seems to be related to searching. Is this, that first (search) is truly defining what can or cannot be searched and second one (rules) only tells Yii what variables are safe for global assignment? If I’m not mistaken, changing only rules() without touching search() does not influence on what really can be searched (i.e. fields removed from that validator still can be searched), though comment says something else. And the real change has to be done in search() function.

[quote name=‘mdomba’ post=‘70740’]Problem 2 - your last line says it all… $(this) is always the current object… but you need the filter input field… in jquery.yiigridview.js that is the variable “inputSelector” (line 42)
[/quote]

I’m trying to get, what you are trying to tell me but without any luck! :(

Do you confirm that $(this) refers to a different object in submit than reset.onclick()? You are giving me exact examples on code lines that I should look for or change, but I don’t get the base - i.e. why $(this).serialize() serializes and sends properly prepared array when form contains at least one field filed and doesn’t do this (sends empty object instead of sending serialized form with all fields values empty) when used inside click function of reset button.

1 - Yii basics :D

the $model->search() does the search…

the controller action that renders the gridview… first creates a new model instance with the search scenario


$model=new Model('search')

then it uses massive assignments to get the values entered in the filter input boxes




		if(isset($_GET['Model']))

			$model->attributes=$_GET['Model'];



and here comes the rules that just tells what attributes can be assigned here… so if you don’t have an attribute as safe ‘on’=>‘search’ that attribute will not be assigned

2 - I understand it’s a bit complicated but it’s really an advanced use of jQuery… note that on the CGridView there is no submit… instead there is an “change” event attached for filter input fields…

Thanks for a brief explanation, but I already know that! :] I was rather asking…

…why I was able to search with attribute that I removed form rules() but not from search() - if I’m not mistaken - cause today I started to doubt everything! :confused: If, what you said is true (and it is, for sure! :]) than I shouldn’t be able to do so, as I removed that attribute from massive assignment. And I was able to block searching that attribute only after I removed its comparison from search() method - again, if I’m not mistaken. Is this normal?

Don’t kill me! :[ That means that with my poor knowledge of jQuery I will not be able to implement… such easy behaviour like resetting filters in GridView along with resetting search form. Argh! :confused:

EDIT: Beside! You’re talking about how GridView processes AJAX request, right? This is really complicated jQuery code done by Yii dev team. And I’m talking about differences between results which are produced by $(this).serialize() in both cases. And as far as I’m not mistaken this function is jQuery or even JS core function, not written by anyone of Yii Dev Team, right? Then differences in submit and reset buttons behaviour and what is produced in serialize() should be related to jQuery/JS not GridView, right?

EDIT: Another problem to be beaten if I would like to iterate through all form fields to build proper resetting data to be submitted to GridView: form name is not precisely defined, it is auto-generated and build-in jQuery code is not catching form itself rather than div surrounding it:


$('.search-form form').submit(function()

Another thing I’ve just noticed. Am I missing something or search-form-ajax-gridview-update-feature stops working when there are no results in the gridview? That would rather be a huge bug. For the first I thought that I messed up something while trying to develop resetting function in gridview’s jquery assets. But I reverted that file back to original one, removed assets and double checked on another browser (FF 3.5, IE 8) and I think I’m right. If I enter to any field a value that after filtering produces no results, you can click Search button till the end of the world and you won’t get neither rounding circle nor GridView update.

Now, tell me that I’m not right, that I messed up something, that it works at your computer and I’m gonna shot myslef! :confused:

NOTE: If an attribute has any rules without the ‘on’ option… then it’s valid for all scenarios…

The filter works even with the empty grid… not sure what happened on your end…

OK, I just worked on the reset button… here is the solution

Add the reset button


<?php echo CHtml::resetButton('Clear!', array('id'=>'form-reset-button')); ?>

Add the jQuery code




$('#form-reset-button').click(function()

{

   var id='document-grid';

   var inputSelector='#'+id+' .filters input, '+'#'+id+' .filters select';

   $(inputSelector).each( function(i,o) {

  	$(o).val('');

   });

   var data=$.param($(inputSelector));

   $.fn.yiiGridView.update(id, {data: data});

   return false;

});



Note that variable id should be replaced by your CGridView id… and that here we assume that filterClass is "filters" (default)

mdomba, you are Yii God and truly awesome person! :] Yii framework itself and its community would never be as it is today without people like you! :} Thank you and congrats!

Thank you Trejder… this “problem” was very interesting to me and I wanted to solve it ;)

Glad, that I’m providing you with some problems a bit more complicated, cause I started to think that I’m flooding this forum with so obvious questions! :confused:

One, little correction. You have to remove return false; from the end of your function or change it into return true; or else your click function will prevent firing of build in reset function making form not to clear it’s contents. At least I needed to have both form and filters to be cleared upon clicking reset button.

And here is an example if someone would want to hide filter boxes in CGridView and develop Reset button only for search form.

The main problem is that build-in (inherited) reset function of Reset button does not work until return false; therefore we have to write our own form cleaning method, with respecting this time (as it was not necessary, when using mdomba’s solution for filters in columns) that submit and reset button are also form elements, but shouldn’t be cleared along with other input and select elements:


    	$('#form-reset-button').click(function()

    	{

            	$('.search-form form input, .search-form form select').each(function(i, o)

            	{

                    	if (($(o).attr('type') != 'submit') && ($(o).attr('type') != 'reset')) $(o).val('');

            	});


            	$.fn.yiiGridView.update('zlecenia-grid', {data: $('.search-form form').serialize()});

            	return false;

    	})

To use this code in your own project you have to change zlecenia-grid to id of your own CGridView.

mdomba, thanks again for putting out some good code. I have been trying to get this to work within two different CJuiTabs. I have given them different id’s and made sure they were pointing to the respective grids. My setup is using renderPartial to different tabs and on these tabs is where I have placed this code.

Unfortunately I have not been able to get the two of them to work together, it is either one or the other. I am doing additional reading on jQuery but would appreciate any help. Is it possible to use this code in the situation I described.

This all depends on the 4th parameter of renderPartial() if it’s false no JS code is rendered… if it’s true it can cause duplicate JS file loading like loading again the jQuery library that in turn brings other problems…

Using jquery code inside other jquery code from Yii is very advanced staff and you need to know how all this works to get it to work together…

There are already many posts/wiki about these problems trying to solve it in different ways…

IMO the simplest solution is to put all the jQuery code in the first view… and on renderPartial include just the PHP code (none JS code)

So in your example… the reset button call CHtml::resetButton(…) would be in the renderPartialView

but the JS handler for it would be in the main view…and for it to work you need to use live()… like




$('#form-reset-button').live('click', function()

...



hello,

newbie here. This solution didnt work for me: input fields keep the searched values, plus I get this error in firebug console:


TypeError: settings is undefined

[Break On This Error] 	


$grid.addClass(settings.loadingClass);

any tip?

thanks