RRListView - a CListView extension to allow Ajax browser history

Hello!

I’m glad you’ve found it useful.

Yes, it is normal that you have multiple ajax loads if you access the hashed link in a new tab - as far as I know, the server will never be sent the hash part of the url (which is for links on the page only), so it can have no knowledge of the state given by the hash. Therefore it sends the normal page given by the url before the hash then, once loaded, the javascript kicks in, generates a hashchange event so that the hash part is checked, and then makes the appropriate ajax requests if it detects that the state given by the hash is not the same as the one given by the url before the hash.

This is therefore a necessary overhead (and you’ll find the same for any other javascript browser history plugins, unless they load everything on the one page and then hide it - in which case it is not true ajax anyway), and retains its utility on the basis that you will only have this overhead if you are using the link with hash from a fresh start - when using back and forward buttons it will not be an issue.

As for the extra ajax requests beyond the initial one, this is due to the fact that you can bind as many ajax handlers as you like, and the changes that they make to the page do not necessarily have to correspond to ajax requests to the base page, or correspond to the same options for that page. Therefore I don’t see any simple way to aggregate requests to the same page into one request without losing the independence between the different handlers.

So, it sounds like you are doing everything right, and the results you are getting are to be expected (I assume the pages are functioning as you want).

I must warn you that I haven’t had time to check the latest versions of the jquery.yiiGridView.js file and update mine appropriately so, although I think it should still work with the latest version, I don’t actually know.

Let me know if you have any more questions.

Thx for answer,

I guess i found some issue. I added the extension to the blog demo from yii and used the filter search.

With the normal gridview and search for the title="test post" i would find my post entry with "test post" in the title.

With your extension it will search for "test+post", this is also added as the filter value.

the ajax request url is wrong when a filter has some "spaces" used

With your extension:

/post/admin?Post[title]=test%2Bpost&Post[status]=&Post_page=1&ajax=yw0

with the normal grid

/post/admin?Post[title]=test+post&Post[status]=&ajax=yw0

the + seems to be escaped in the ajax request. Or the bbq url generation is, because when i change the hash url from

post/admin#Post[title]=test+post

to

post/admin#Post[title]=test%20post it will show the correct results.

Ok, thanks - that does sound like an issue. I’ll have a look at it when I get some time.

I had some time and tried some different approach, and implemented it directly in the jquery.yiigridview.js file.

And it works ;)

I limited the additional ajax request to only one which has to be done when a url with bbq url’s is pasted into a new browser window.

I tested also multiple gridview’s on one page as well as filter changes.

So far I can’t find no problems/bugs.

Currently the bqq usage for ajax requests is enabled by default. It can be changed via the settings which needs a public property in CGridview (need to be extended).

For testing just replace the original file in the asset folder.

rename the file to from .js.txt => .js (attachment was permitted).

Remarks are Welcome

regards Horizons

Ok, that’s good if it works for you.

The reason I had originally separated the hash handling from the gridview was so that other widgets or functions could also use the hash in the same way without having to have a hashchange handler for each one. In my logic a widget could initiate a hash change, and also pass a callback to be fired if needed (if one of its parameters were concerned), so that if a change in the hash happened which did not concern that particular widget we wouldn’t have to fire multiple hash change events anyway, and we wouldn’t have to duplicate code in checking whether the change concerned the widget.

If you try my modification it is exactly what you mean modifications in the #yw0 part of the url would make changes to the #yw0 widget only.

Just add 2 gridviews on the same page and test it out.

I was talking more about different widgets (or even custom ajax updates).

But I will certainly have a look and get back to you. Unfortunately I’m in the middle of a project at the moment so don’t have time to look yet.

Updated the gridview js file from yii version 1.1.7 with my bbq modifications.

Still no remarks or suggestions?

Sorry - I will get back to you - it’s just that at the moment I’m in the middle of a project that I need to finish.

Horizons i think your code is great. 1 last suggestion, please add the code to update the grid using the advance search form :)

@pcs2112 good to hear that my solution also works for others.

But i don’t understand your request because if i use the advanced search form on my site the gridview will get rendered correctly and after a successful ajax update the bbq url part is also correctly. And if i use the forward/backwards buttons of my browser the advanced search form options also get the values accordingly.

maybe some code example helps.




<?php


$basepath=Yii::app()->getBasePath();


Yii::app()->clientScript->registerScript('search', "

$('.search-button').click(function(){

	$('#search-form-outer').toggle();

	return false;

});

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

	$.fn.yiiGridView.update('disseminations-grid', {

		data: $(this).serialize()

	});

	return false;

});

");

?>

<div style="margin:10px">

<?php

$searchform = CHtml::link('Advanced Search','#',array('class'=>'search-button'));

$searchform.='<div id="search-form-outer" style="display:none">';

$searchform.='<div id="search-form" class="search-form">';


$searchform.=$this->renderPartial('_search',array(

	'model'=>$model,

    'width'=>'600px'

),true);


$searchform.='</div><!-- search-form -->';  

$searchform.='</div>';


echo $searchform;




$absolute_url=Yii::app()->createAbsoluteUrl('//');

$updateSearchFormscript =<<<EOP


function updateSearchForm(id,result){

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

        $.fn.yiiGridView.update(id, {

            data: $(this).serialize()

        });

        return false;

    }); 

}


EOP;


Yii::app()->getClientScript()->registerScript('updateSearchForm',$updateSearchFormscript);


$export_links=CHtml::openTag('div',array('class'=>'exportbuttons'));

$export_links.=CHtml::Link('&nbsp;','',array('class'=>'excelexport tooltipleft','title'=>Yii::t('Global','Export to Excel'),'onclick'=>"location.replace($.param.querystring($.fn.yiiGridView.getUrl('disseminations-grid'),'export=1'));"));

$export_links.=CHtml::closeTag('div');


$pageSize=Yii::app()->user->getState('limit',Yii::app()->params['defaultPageSize']);

$pageSizeSelect=CHtml::openTag('div',array('class'=>'pageSizeSelect'));

$pageSizeSelect.=CHtml::Tag('span',array(),'Items per page:');

$pageSizeSelect.=CHtml::dropDownList('Disseminations[limit]',$pageSize,array(1=>1,5=>5,10=>10,20=>20,50=>50,100=>100),array(

'onchange'=>"$.fn.yiiGridView.update('disseminations-grid',{ data:{limit: $(this).val() }})",

));

$pageSizeSelect.=CHtml::closeTag('div');


$template="<div class='gridviewoptions'>{summary} $pageSizeSelect $export_links <br class=\"clearfloat\" /></div>\n{items}\n{pager}";


Yii::app()->user->setReturnUrl($this->createUrl('admin'));


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

	'id'=>'disseminations-grid',

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

    'ajaxUpdate'=>'disseminations-grid,search-form',

    'afterAjaxUpdate'=>'updateSearchForm',

    'filter'=>$model,

    'cssFile'=>Yii::app()->baseUrl .'/css/grid2.css',

    'pager' => array('cssFile' => Yii::app()->baseUrl . '/css/grid2.css'),

    'filter'=>$model,

    'filterPosition'=>'body',

    'template' => $template,

	'columns'=>array(

    --- columns ---

	),

));


?>


</div>



awesome thanks for the reply your modification of the yiigridview works perfectly :). I added jquery blocking to make the loading of the grid similar to what you see in google when enterinf a new search or change the page.

I’m looking to enable back button with clistview… i think this will work but only for cgridview, not for clistview. Any planing for adding support for that widget?

if you publish this extension to extension repository ,it will be fine ::)

Hello!

I will also upload the same thing for CListView fairly soon, but I don’t have the time to do it at the moment because my brother is visiting me.

Hi, thanks for that. I’ve tested your clistview widget but it doesn’t work for me. When i use the pagger, then the links appear to be incorrectly encoded. Some like index.php?r=controller%3ffunction. Seems like it’s encoding the /, and then i get an error

Ok, for those impatients :P

Here is the latest version of GridView, which I haven’t yet had time to document or package nicely (and no instructions yet).

I will do a proper packagae, and upload as an extension for both ListView and Gridview in the next week or so.

We are impatient for the clistview too!!! lol

I have one suggestion, please, do the same for the clistview. Im not very good with js code

Ok, so here are both the CListView and CGridView extensions 1696

RRViews.zip
.

I haven’t yet included any documentation, but they are fairly well commented.

They are perhaps not the perfect solution - it really depends on what you want them for. I have preferred to assume integration between query parameters in the browser bar url and for the ListView/GridView (I’m perhaps obsessed with the symmetry between ajax and non-ajax requests, so I want my querystrings to represent the correct state as a whole as it may be accessed with or without javascript).

This does mean that:

  • you cannot have different listviews/gridviews on the same page that use the same GET parameters (so for two listviews for example, you would have to change the pagination and sorting GET vars for them to work together)

  • all the widgets on the page must be accessed from the same action that generated the page (this can easily be got around but perhaps with a loss of coherence between the different parameters)

I have changed my approach for parsing parameters from updaters - previously I imitated the original js implementation by extracting the parameters from the normal link, and merging them with those in the key link for the widget, perhaps overriding them with parameters from the location querystring or the hash fragment, but I believe this can lead to inconsistencies.

So now, when binding the links, I assume a parameter (which means it is harder to add custom updaters, especially ones which change more than one parameter). Thus for paging links I will only extract the pageVar parameter, and for sorting links likewise.

Handling the input ajax (for the gridview) seems good, with entering text in one input only adding one parameter, and removing text from one not resulting in others being erased.

The hash handling logic is still in a separate javascript file, and could probably do with an overhaul (the error mechanism doesn’t work very well), but that will have to come later as it still works.

The advanced search function (which is not actually part of CGridView) can be included in this, simply by manually defining the selector for the input updaters.

So an example of the parameters to add to the widget’s construction would be:


'ajaxUpdate' => $searchId,

'inputUpdater' => '#'.$gridId.' .filters input, #'.$gridId.' .filters select, #'.$searchId.' form input, #'.$searchId.' form select',

where $searchId is the id that I have added to the div containing the advanced search form, inputUpdater is the selector for the input elements that should trigger an ajax update (a custom field that I have added, normally calculated automatically in the javascript file, but defined manually here so as to include the inputs from the advanced search form) and $gridId is the id given to the gridview widget.

Adding $searchId to the ajaxUpdate means that whenever the grid is updated, the search form is also updated.

Personally I also think that the submit button should be hidden using javascript, since it has no function here.

I hope this works for you all. Please let me know if there are any bugs.

Once I have ironed everything out I will make it available as an extension.

I should have said - to use, jus unzip into the extensions folder, then for your widget use ‘ext.RRListView’ etc