I have a sub page, which is loaded via ajax, and there’s a grid view on that fragment. When the entire subpage reloads via ajax, the javascript yiiGridview plugin rebinds all of sorting and paging links:
I think you thought serving the ajax request with renderPartial and set $processOutput to false. But that won’t work. The problem is that all gridview functionality binded to the current DOM element except paging and sorting functions, so when you reinsert the same grid you have to initialize it, otherwise you can’t use the built in ajax update, and when you do that, that link’s javascript click event will be binded to the document one more time, because that events didn’t disappear when you removed the grid from the DOM.
UPDATE
Ok, I’ve just checked, the code again. There’s no functionality or settings what is bounded to the current DOM element. In this case, the problem is little tricky, because every grid should be initialized only one time. This can be solved by set processOutput false after the first time, which is not good, if you have an activeform in the view, you have to intialize it, and how do you know on server side, that you will have to initialize the gridview or not?
on the other side if the grid should be rendered by ajax even the first time, you can always have a custom JS logic that would send a special parameter just the first time to the action and there you can check for that parameter and decide if to render or renderpartial
Yes, that view always requested via ajax, so the first option is not good.
The second can solve the problem, but I think this is not a ‘clean’ solution, and not general. If I could do that, I modify the gridview plugin, because I think that is the right place, but that is a framework provided code.
I have a complete admin system depending on ajax calls and gridviews, so I should find the right place.
I don’t see why would that be “not clean” it’s not a hack or anything like that…
if you have a good solution to be implemented in the core you can suggest it… just don’t come with the undelegate (off) before delegate (on) solution as this has already been extensively discussed and is not proper.
We bind events when the the current id is not present in the settings array. And in the event callbacks, we use the settings array instead of a local copy, because in this case we can overwrite grid settings in a future request.
I can’t attache JS files (“Error You aren’t permitted to upload this kind of file”).
How can I share it?
Here’s the diff (line numbers are from a large concatenated file).
# This patch file was generated by NetBeans IDE
# It uses platform neutral UTF-8 encoding and \n newlines.
--- Remotely Modified (Based On HEAD)
+++ Locally Modified (Based On LOCAL)
@@ -3304,7 +3304,8 @@
return this.each(function () {
var $grid = $(this),
id = $grid.attr('id'),
- inputSelector = '#' + id + ' .' + settings.filterClass + ' input, ' + '#' + id + ' .' + settings.filterClass + ' select';
+ inputSelector = '#' + id + ' .' + settings.filterClass + ' input, ' + '#' + id + ' .' + settings.filterClass + ' select',
+ initialized = gridSettings[id];
settings.tableClass = settings.tableClass.replace(/\s+/g, '.');
if (settings.updateSelector === undefined) {
@@ -3313,8 +3314,10 @@
gridSettings[id] = settings;
+ if(!initialized) {
+
if (settings.ajaxUpdate.length > 0) {
- $(document).on('click', settings.updateSelector, function () {
+ $(document).on('click', gridSettings[id].updateSelector, function () {
$('#' + id).yiiGridView('update', {url: $(this).attr('href')});
return false;
});
@@ -3322,15 +3325,15 @@
$(document).on('change', inputSelector, function () {
var data = $(inputSelector).serialize();
- if (settings.pageVar !== undefined) {
- data += '&' + settings.pageVar + '=1';
+ if (gridSettings[id].pageVar !== undefined) {
+ data += '&' + gridSettings[id].pageVar + '=1';
}
$('#' + id).yiiGridView('update', {data: data});
});
- if (settings.selectableRows > 0) {
+ if (gridSettings[id].selectableRows > 0) {
selectCheckedRows(this.id);
- $(document).on('click', '#' + id + ' .' + settings.tableClass + ' > tbody > tr', function (e) {
+ $(document).on('click', '#' + id + ' .' + gridSettings[id].tableClass + ' > tbody > tr', function (e) {
var $currentGrid, $row, isRowSelected, $checks,
$target = $(e.target);
@@ -3343,23 +3346,23 @@
$checks = $('input.select-on-check', $currentGrid);
isRowSelected = $row.toggleClass('selected').hasClass('selected');
- if (settings.selectableRows === 1) {
+ if (gridSettings[id].selectableRows === 1) {
$row.siblings().removeClass('selected');
$checks.prop('checked', false);
}
$('input.select-on-check', $row).prop('checked', isRowSelected);
$("input.select-on-check-all", $currentGrid).prop('checked', $checks.length === $checks.filter(':checked').length);
- if (settings.selectionChanged !== undefined) {
- settings.selectionChanged(id);
+ if (gridSettings[id].selectionChanged !== undefined) {
+ gridSettings[id].selectionChanged(id);
}
});
- if (settings.selectableRows > 1) {
+ if (gridSettings[id].selectableRows > 1) {
$(document).on('click', '#' + id + ' .select-on-check-all', function () {
var $currentGrid = $('#' + id),
$checks = $('input.select-on-check', $currentGrid),
$checksAll = $('input.select-on-check-all', $currentGrid),
- $rows = $currentGrid.children('.' + settings.tableClass).children('tbody').children();
+ $rows = $currentGrid.children('.' + gridSettings[id].tableClass).children('tbody').children();
if (this.checked) {
$rows.addClass('selected');
$checks.prop('checked', true);
@@ -3369,14 +3372,15 @@
$checks.prop('checked', false);
$checksAll.prop('checked', false);
}
- if (settings.selectionChanged !== undefined) {
- settings.selectionChanged(id);
+ if (gridSettings[id].selectionChanged !== undefined) {
+ gridSettings[id].selectionChanged(id);
}
});
}
} else {
$(document).on('click', '#' + id + ' .select-on-check', false);
}
+ }
\ No newline at end of file
});
},
In my version this will happen when you reinitialize a gridview:
init: function (options) {
var settings = $.extend({
ajaxUpdate: [],
ajaxVar: 'ajax',
pagerClass: 'pager',
loadingClass: 'loading',
filterClass: 'filters',
tableClass: 'items',
selectableRows: 1
// updateSelector: '#id .pager a, '#id .grid thead th a',
// beforeAjaxUpdate: function (id) {},
// afterAjaxUpdate: function (id, data) {},
// selectionChanged: function (id) {},
// url: 'ajax request URL'
}, options || {});
return this.each(function () {
var $grid = $(this),
id = $grid.attr('id'),
inputSelector = '#' + id + ' .' + settings.filterClass + ' input, ' + '#' + id + ' .' + settings.filterClass + ' select',
initialized = gridSettings[id];
settings.tableClass = settings.tableClass.replace(/\s+/g, '.');
if (settings.updateSelector === undefined) {
settings.updateSelector = '#' + id + ' .' + settings.pagerClass.replace(/\s+/g, '.') + ' a, #' + id + ' .' + settings.tableClass + ' thead th a';
}
gridSettings[id] = settings;
});
},
As you can see only some settings initialization and settings overwrite will happen, when you call $(’#some_id’).yiiGridView({…options…}), the plugin registering is necessary as the grid can be new in the DOM.
if(!initialized) {
// do initialization
} // else nop
is the same.
Return at any other point of the init method is wrong, as the init function does 2 thing: add/update current settings, and bind events. In my version we don’t bind events when the grid was initialized before. The init method should be invoked, as only the plugin itself can decide what to do in different situation, for example overwrite settings.
If we know that grid settings are not changed between ajax calls, then yes, all the $(’#some_id’).yiiGridView({…}) initialization is redundant and unnecessary. In my case the settings won’t change.
If this is a by design decision, we should examine the possibility of solving this issue at server side by not registering the init script in the widget, but at first this doesn’t seem a good solution. What do you think?
Makes no sense to change any settings between sorting/filtering/pagination calls…
As for the server side that would be the best solution, but I still got no idea on how to do it… i.e. how to detect that a grid has already been initialized…
No, the problematic ajax calls are those ones which aren’t triggered by the grid. The grid generated calls (paging/sorting/fitering/updating) don’t reinitialize the grid itself, so the settings can’t change as you said.
But other calls, for example there are two different menu which calls the same action but with different params, can cause settings change. Theoretically.
In this case we don’t have to decide on server side.