Yii 1.1: rrviews

An extension to allow automatic hash history (ajax browser back button support) in the CListView and CGridView widgets
30 followers

This extension is to allow users of the CListView and CGridView Zii components to take advantage of the Ajax features of them, whilst maintaining good practice with regards to browser navigation - enabling the browser back button and enabling the address bar url to accurately represent the current page state (which will then be replicated on re-opening the page).

Basically it uses JQuery and the BBQ JQuery plugin to maintain a hash history of specific Ajax actions.

For example, clicking on the ajax page 2 of a list on the page "www.site.com/webpage" will append something like "#page=2" to the url, giving "www.site.com/webpage#page=2", as well as loading the second page through ajax, as usual. And if you then copy this link and paste it somewhere else, you will see that your page will automatically navigate to the second page of the list.

Having just upgraded to Yii 1.1.9, I am aware that the core Yii gridview javascript has been completely revamped (this may be true for listview too), and as such, this extension will not yet work with version 1.1.9. I am working on an updated version (that will also have to completely break with past versions) and will upload it when it is ready.

Requirements

Yii 1.1.7 (this started out using 1.1.5, but has been upgraded every time to take into account differences in the PHP classes and JS files). It shouldn't be too complicated to downgrade it if required (changes to the original Yii version are clearly marked), but I will not personally be doing this.

Usage

Simply change the class where you usually put your zii.widgets.CListView or zii.widgets.grid.CGridView widgets to ext.RRListView or ext.RRGridView (assuming that ext is defined as the alias for your extensions path and that you have extracted the class files directly into your extensions path) and set your parameters as usual (all the usual CListView/CGridView options should work as normal). e.g.

$this->widget('ext.RRGridView', array(
    'dataProvider' => $dataProvider,
    'columns' => array(
        'id',
        array(
            'class' => 'CButtonColumn',
        ),
    ),
));

This should work right out of the box, with hash history enabled by default.

There are obviously several options:

  • enableJavascript is set to true by default, and simply allows you to not include all the usual javascript files if you don't want to use any javascript features;

  • extensionsAssets is not set by default, but allows you to explicitly set the location of the extension's assets if needed (if you don't put them in the default ext.assets for example);

  • hashHistory is set to true by default and determines whether hash history will be enabled. If set to false, we fall back on to the default (Zii) ajax handling for CListView and CGridView;

  • updaters is an array of updaters that should trigger ajax changes (to be reflected in the hash history). Each item should consist of 'selector' => JQuery selector for the elements triggering the change, and 'getParams' => array('a', 'b', 'c'), an array of the GET parameters to be taken into account from the hrefs of the selected anchor elements (NB in the case of input elements this attribute should not be set - the params will be determined from the input names). The array is populated by default on initialisation with page and sort elements if pagination and sorting are enabled, and their selectors are generated automatically in the javascript if not overridden in these options.

Below is an example of all these options in (mutually exclusive) action:

$this->widget('ext.RRGridView', array(
    'dataProvider' => $dataProvider,
    'enableJavascript' => false,
    'extensionAssets' => Yii::app()->getAssetManager()->publish(Yii::getPathOfAlias('ext.otherfolder.assets')),
    'hashHistory' => false,
    'updaters' => array(
        'sort' => array(
            'selector' => '#gridId thead th a',
            'getParams' => array('mySortVar'),
        ),
        'advancedSearch' => array(
            'selector' => '#advanced-search form input, #advanced-search form select',
        ),
    ),
    'columns' => array(
        'id',
        array(
            'class' => 'CButtonColumn',
        ),
    ),
));

Caveats

I have tested these extensions slightly, but I'm afraid that I have not tested them extensively. I don't know whether they work in all circumstances (I have tried to code for all circumstances) and all browsers (I hate playing around in IE). However, if you do find bugs and let me know about them (preferably with relevant details like error messages from Firebug etc, or even pinpointing the location), then I will fix them if possible, and depending on my workload.

Certain things should be taken into account:

  • I originally made these extensions for me - therefore they reflect my view of how things should work, which may not be your view;

  • I have tried to make as few modifications as possible and couple as loosely as possible to the original Zii versions of these widgets, so that future changes in them should, hopefully, not impact this extension;

  • The javascript file used for implementing hash history in the browser overrides the standard jquery.yiilistview.js (and GridView) inclusion and therefore the $.fn.yiiListView namespace. Currently it includes the Zii version, backs up its attributes, overrides the initialisation method then restores the original object's methods plus some helpers, so this should only be a problem if the original Zii javascript initialisation method changes;

  • The logic for monitoring hash changes is kept in a separate javascript file (the ListViews simply register the parameters to be monitored and callback functions with this script) - this means that the actual hash monitoring logic may be substituted (the original file probably needs a good rewrite anyway), and also that you may register your own parameters to be monitored if you want to dig into the file;

  • my philosophy with regards to this hash history is not necessarily standard - I have taken the view that the hash parameters should reflect and integrate the query parameters. This means that when calculating the ajax request for the address 'www.site.com/webpage?sort=id#page=2', I take into account the sort parameter in the querystring (unless it is overridden in the hash) and merge it with the page parameter in the hash, so send a request for the second page of the id sort. I therefore use the window.location url as the basis for my ajax request rather than the keys div generated by the Zii widget. This means two things - first of all, all widgets on the page must use the current page location as their route for ajax updates; and widgets cannot use GET parameters with the same name. This is something that could be overcome by ignoring querystring parameters in the current location, using the keys div location instead of the current location and prepending the widget id to each of its GET parameters. This is something that I might consider implementing as an option at a future date.

Resources

One thing that has been asked before, and I think is a good example for this extension, is how to include the default Gii advanced search (on the admin view) in the ajax updating. This is very simple with this extension - you just need to assign an id to the advanced search container, add this in the options of the widget as a container to be updated, and add a JQuery selector for it, like so:

<?php $searchId = 'advanced-search';
echo CHtml::link('Advanced Search', '#', array('class' => 'search-button')); ?>
<div id="<?php echo $searchId; ?>" class="search-form" style="display: none">
    <?php $this->renderPartial('_search',array(
        'model' => $model,
    )); ?>
</div><!-- .search-form -->
$this->widget('ext.RRGridView', array(
    'dataProvider' => $model->search,
    'filter' => $model,
    'ajaxUpdate' => $searchId,
    'updaters' => array(
        'advancedSearch' => array(
            'selector' => '#'.$searchId.' form input, #'.$searchId.' form select',
        ),
    ),
    'columns' => array(
        'id',
        array(
            'class' => 'CButtonColumn',
        ),
    ),
));

I would also suggest that you remove the style="display: none" from the Html and add some javascript on page load to hide the search div (and also the submit button) which then allows for graceful non-javascript degradation, and also prevents the problem of the search form being reinserted as hidden with every ajax update.

The forum post that this extension started out as can be found here, and any questions or bug reports should be posted there.

Total 5 comments

#8635 report it
RedRabbit at 2012/06/15 01:42am
opening with current hashtag

@rokam

Thanks!

It is impossible to open the grid directly with the current hash tag (on page load I mean), since the hash part of a url is never sent to the server, so the server cannot know what part of the grid to load, that is why the default page is loaded, then ajax kicks in on the client side to load the appropriate grid depending on the hash tag.

#8633 report it
Rokam at 2012/06/14 11:17pm
Thank you

I think this feature should be incorporated to next release.

I do only think that the grid should open with the current hashtag and not open with default and then load the hashtag.

Really great! :D

#5591 report it
RedRabbit at 2011/10/23 04:07am
@alrissala

Please use the formum post for this. I only use pathFormat and am not aware of any problem withi it.

#5581 report it
alrissala at 2011/10/21 02:14pm
doesn't work when the urlManager urlFormat property set to "path"

Hi, first thanks for this extension but i noticed that it doesn't work when the urlManager urlFormat property set to "path"

Yii 1.1.8, windows 7, wamp server

#4159 report it
chux at 2011/06/10 09:07am
Great

I'm using this for one project, and works great. Thanks for this extension

Leave a comment

Please to leave your comment.

Create extension
Downloads