Forum

November 2nd, 2014
A A A
Avatar

Lost password?
Advanced Search

— Forum Scope —




— Match —





— Forum Options —





Minimum search word length is 3 characters - maximum search word length is 84 characters

The forums are currently locked and only available for read only access
sp_Feed Topic RSS sp_TopicIcon
usage of jQuery events in jqGrid together with callback functions used currently
02/01/2012
01:42
Avatar
OlegK
Germany
Member
Members
Forum Posts: 1255
Member Since:
10/08/2009
sp_UserOfflineSmall Offline

Hello Tony,
Hello all who use or develop jqGrid,

Happy New Year!

I want write here about one important design problem in jqGrid and about the suggestion to use jQuery events in jqGrid. Currently only reloadGrid which can be triggered from outside of jqGrid code uses jQuery events.

I wrote about the usage of jQuery events in jqGrid here and here, but I decide to describe the advantages of the approaches more detailed in the feature request and demonstrate all on the examples of existing jqGrid code.

Let us to examine some existing code fragments of jqGrid. Just some examples:

  • ovewriting of gridComplete inside of internal selectionPreserver function used in case of trigger reloadGrid with {current:true}. It's used in navGrid with refreshstate:true option.
  • introduction of _complete callback together with gridComplete which are called inside of internal updatepager function. It's used inside of setFrozenColumns.
  • ovewriting of resizeStop inside of setGroupHeaders.
  • ovewriting of resizeStop, onSortCol and _complete inside of setFrozenColumns.

There are some other internal problems in jqGrid

  • the original resizeStop callback which are overwritten in setGroupHeaders will be NOT restored in destroyGroupHeader. If one calls setGroupHeaders and destroyGroupHeader many times the all instanses of the previous setGroupHeaders will be used in the long chain of resizeStop callbacks.
  • synchronization between formatter: 'actions', inlineNav and navGrid buttons (at least "Delete" button). The problem is that the inline or navigator buttons (div.ui-inline-edit, div.ui-inline-save, div.ui-inline-del, div.ui-inline-cancel. "..._ilsave", "..._ilcancel", "..._iladd", "..._iledit" ...) should be enabled, disabled, hide or shown depend on selected row and the row state (whether the row is in the inline editing). It seems me difficult to implement synchronization between all formatter: 'actions', inlineNav and navGrid in one grid using the current design pattern.

There are exist close problems if one write a complex project which uses jqGrid problem. One want to do some common actions inside of loadComplete, onSelectRow etc callbacks and implements only some additional spectfic action in the callbacks handles of specific grids. The problem is that jqGrid allows only one callback per grid so one can't define two loadComplete handler (one general for the project and one specific for the grid can be defined). As the result one recive more code duplicates and the code will be more difficult to maintain.

What I suggest is to use jQuery events together with existing callback functions. I'll explain what I mean on examples. The last two lines of internal updatepager function of grid.base.js looks currently as the following

if($.isFunction(ts.p.gridComplete)) {ts.p.gridComplete.call(ts);}
if($.isFunction(ts.p._complete)) {ts.p._complete.call(ts);}

Another line of setRowData method uses also _complete:

if($.isFunction(t.p._complete)) {t.p._complete.call(t);}

The callback _complete was introduced for the usage inside of setFrozenColumns method (see here) in the following form:

if ($.isFunction($t.p._complete)) {
    $t.p.orgEvents.complete = $t.p._complete;
} else {
    $t.p.orgEvents.complete = null;
}
...
$t.p._complete = function() {
   ...
};

I suggest to replace the usage of .p._complete inside of updatepager (see above) to the usage of new jQuery event:

if($.isFunction(ts.p.gridComplete)) {ts.p.gridComplete.call(ts);}
$(ts).triggerHandler("jqGridGridComplete");

In the case the usage of t.p._complete in the setRowData method can be now as following

$(t).triggerHandler("jqGridGridComplete");

and the code inside of setFrozenColumns method just as

$($t).bind('jqGridGridComplete.setFrozenColumns', function () {
    ... // the code from the body of $t.p._complete = function() {...}
});

In the above example I used setFrozenColumns namespace (see "Using Namespaces" part of jQuery.unbind documentation) which simplifies the code inside of destroyFrozenColumns for unbinding of three events jqGridResizeStop.setFrozenColumns, jqGridOnSortCol.setFrozenColumns and jqGridGridComplete.setFrozenColumns to just one line

$(this).unbind('.setFrozenColumns');

In case of usage events with parameters like resizeStop one can include trigger of new jqGridResizeStop event before or after the p.resizeStop.call (see the line):

$(ts).triggerHandler("jqGridResizeStop", [nw, idx]);
if($.isFunction(p.resizeStop)) { p.resizeStop.call(this,nw,idx); }

The usage of jqGridResizeStop inside of setFrozenColumns method can be like

$($t).bind('jqGridResizeStop.setFrozenColumns', function (e, w, index) {
    var rhth = $(".ui-jqgrid-htable",$t.grid.fhDiv);
    $("th:eq("+index+")",rhth).width( w );
    var btd = $(".ui-jqgrid-btable",$t.grid.fbDiv);
    $("tr:first td:eq("+index+")",btd).width( w );
});

The code can replace the existing code (see here)

if ($.isFunction($t.p.resizeStop)) {
    $t.p.orgEvents.resizeStop = $t.p.resizeStop;
}
$t.p.resizeStop = function(w, index)
{
    var rhth = $(".ui-jqgrid-htable",$t.grid.fhDiv);
    $("th:eq("+index+")",rhth).width( w );
    var btd = $(".ui-jqgrid-btable",$t.grid.fbDiv);
    $("tr:first td:eq("+index+")",btd).width( w );

    if ($.isFunction($t.p.orgEvents.resizeStop)) {
        $t.p.orgEvents.resizeStop.call($t, w, index);
    } else {
        $t.p.orgEvents.resizeStop = null;
    }
};

The most advantage of the approatch is that the code of destroyFrozenColumns will work much more safe and clear because one will not need to restore of the original resizeStop callback.

In the current implementation of setGroupHeaders and setFrozenColumns methods the original resizeStop callback and destroyGroupHeader, destroyFrozenColumns should be restored. If every from the methods setGroupHeaders and setFrozenColumns will just save previous value of resizeStop callback and destroyGroupHeader and destroyFrozenColumns restore the saved value then the results will depend on the order of the calls of the calls of the methods. One can have different results or even be not able to restore the original resizeStop callback.

I modified the existing code of grid.base.js and grid.custom.js from jqGrid 4.3.1 to grid.base-events.js and grid.custom-events.js to demonstrate the approatch. One can see on the demo that both group headers and frozen columns works exactly as before with the modifications.

To demonstrate the advantage of the approatch I created one more demo. The demo has both advanced searching and searching toolbar. If one set in the searching toolbar some filter, for example, "test" for 'Client' column and "300" for 'Amount' column the data in grid will be filtered. The filter set postData.filters and so if the Advanced Searching dialog will be opened it will be filled with the corrent filter. Now if one changes "300" for 'Amount' to '200' and click "Find" button the grid body will be updated corresponds to the new filter and the searching toolbar will be updated too. If will be done with respect of createSearchingToolbarSynchronization method which uses the jqGridLoadComplete event. The original code without the usage of the method will update only the grid body without updating of the searching toolbar. I think the code from the createSearchingToolbarSynchronization method could be included in the main code of filterToolbar method. I don't make that to show more clear that using new jQuery style events like jqGridLoadComplete one are able to write jqGrid plugins more easy.

I hope that the above examples demonstrate clear enough the advantage of the approatch.

Best regards
Oleg

P.S. A little more sofisticated case of the usage of jQuery style events one find with jqGridBeforeSelectRow event. The result property of the event will hold the return value from the event handler. The triggerHandler or trigger return the value.

04/01/2012
08:31
Avatar
tony
Sofia, Bulgaria
Moderator
Members

Moderators
Forum Posts: 7721
Member Since:
30/10/2007
sp_UserOfflineSmall Offline

Hello Oleg,

Very practical. Thanks.

Could you please make the changes into the GitHub, so that I canb merge it?

Thanks Again.

Kind Regards

Tony

For professional UI suites for Java Script and PHP visit us at our commercial products site - guriddo.net - by the very same guys that created jqGrid.

05/01/2012
04:15
Avatar
OlegK
Germany
Member
Members
Forum Posts: 1255
Member Since:
10/08/2009
sp_UserOfflineSmall Offline

Hello Tony,

I sent the pull request. It's still not full, but it's difficult to make all changes at once.

Best regards
Oleg 

05/01/2012
13:09
Avatar
tony
Sofia, Bulgaria
Moderator
Members

Moderators
Forum Posts: 7721
Member Since:
30/10/2007
sp_UserOfflineSmall Offline

Oleg,

Thanks. I wiil make first the changes in the grid and then in the other modules in order to test them carfully.

This is not a little change.

Best Regards

Tony

For professional UI suites for Java Script and PHP visit us at our commercial products site - guriddo.net - by the very same guys that created jqGrid.

05/01/2012
14:36
Avatar
OlegK
Germany
Member
Members
Forum Posts: 1255
Member Since:
10/08/2009
sp_UserOfflineSmall Offline

Hello Ton,

I full agree Tony that it's a lot of changes and there should be carefully tested of cause.

The minimal set of changes would include grid.base.js, grid.custom.js and jquery.fmatter.js because all the modules used _complete method.

I personally find the events from grid.inlinedit.js also very important to be able to implement clean and simple synchronization between formatter: 'actions', inlineNav, navGrid and manual inline editing (for example in double-click event).

The list of events from the pull request is the following:

grid.base.js:
--------------
afterInsertRow jqGridAfterInsertRow beforeProcessing jqGridBeforeProcessing beforeRequest jqGridBeforeRequest beforeSelectRow jqGridBeforeSelectRow - can return false or 'stop' to prevent row selection gridComplete jqGridGridComplete, jqGridAfterGridComplete loadBeforeSend loadComplete jqGridLoadComplete, jqGridAfterLoadComplete loadError jqGridLoadError onCellSelect jqGridCellSelect ondblClickRow jqGridDblClickRow onHeaderClick jqGridHeaderClick onPaging jqGridPaging - can return 'stop' to prevent change the page onRightClickRow jqGridRightClickRow onSelectAll jqGridSelectAll onSelectRow jqGridSelectRow onSortCol jqGridSortCol - can return 'stop' to prevent sorting of the column resizeStart jqGridResizeStart resizeStop jqGridResizeStop onShowCol jqGridShowHideCol onHideCol jqGridShowHideCol onLeftKey jqGridKeyLeft onRightKey jqGridKeyRight onEnter jqGridKeyEnter onSpace jqGridKeySpace grid.celledit.js ---------------- beforeEditCell jqGridBeforeEditCell afterEditCell jqGridAfterEditCell onSelectCell jqGridSelectCell beforeSaveCell jqGridBeforeSaveCell beforeSubmitCell jqGridBeforeSubmitCell afterSubmitCell jqGridAfterSubmitCell afterSaveCell jqGridAfterSaveCell errorCell jqGridErrorCell afterRestoreCell jqGridAfterRestoreCell grid.custom.js -------------- beforeSearch jqGridBeforeSearch afterSearch jqGridAfterSearch beforeClear jqGridBeforeClear afterClear jqGridAfterClear grid.grouping.js ---------------- onClickGroup jqGridClickGroup grid.import.js -------------- importComplete jqGridImportComplete grid.inlinedit.js ----------------- oneditfunc jqGridEditRow aftersavefunc jqGridAfterSaveRow successfunc jqGridSuccessSaveRow errorfunc jqGridErrorSaveRow afterrestorefunc jqGridAfterRestoreRow grid.subgrid.js --------------- subGridBeforeExpand jqGridSubGridBeforeExpand subGridRowExpanded jqGridSubGridRowExpanded subGridRowColapsed jqGridSubGridRowColapsed

The events which not return any value are very simple to test. The parameters are the same as in the corresponding existing callback function. I added additional parameters in some places, but it's not so important. The most important is to verify in which variable (ts or $t) the DOM of the grid are saved. So that there are no cut & pased error.

The code for events which can retuturn any value to stop the event is a little more complex. If the event handler don't return any value then the returned value of the triggerHandler will be undefined. To be conform with the existing callback I included in the code the possibility to return the same value (true, false or 'stop') to stop the event. If the original callback uses true or false to stop the corresponding action I added everywhere additional possibility to return 'stop' string. The problem is that the code of trigger or triggerHandler interpret the false value not olny as just one return value, but additionally call event.preventDefault(); and event.stopPropagation(); (see here in jQuery 1.7.1, here in jQuery 1.4.3 or here in jQuery 1.3.2). Because we use everywhere the events bound directly to the grid (table element) no event propagation is needed. So the call of event.preventDefault() and event.stopPropagation() has absolutely no meaning. Nevertheless I included the common way to return the 'stop' string. I think it could be practical in case of the usage in the future the events which can be do propagated.

The corresponding code looks like in the follwing example. The original code

cSel = true;
if($.isFunction(ts.p.beforeSelectRow)) { cSel = ts.p.beforeSelectRow.call(ts,ptr[0].id, e); }
...
if(cSel === true) {
    ... do selection
}

I modified to

cSel = $(ts).triggerHandler("jqGridBeforeSelectRow", [ptr[0].id, e]);
cSel = (cSel === false || cSel === 'stop') ? false : true;
if(cSel && $.isFunction(ts.p.beforeSelectRow)) { cSel = ts.p.beforeSelectRow.call(ts,ptr[0].id, e); }
...
if(cSel === true) {
    ... do selection

The statement

cSel = (cSel === false || cSel === 'stop') ? false : true; 

set cSel variable to true like it was in the original code in case of any values returened from the event handler with exception false or 'stop'. So for example if no event handler is bound to the event jqGridBeforeSelectRow the cSel variable will be first set to undefined (returned by triggerHandler) and then it will be fixed to the true like it was in the old code.

Exactly the same way I used in all places of the code which I posted with small modifications sometime.

I hope my descriptions here will help you better to undesrtand and find better test cases to test the suggested code.

Best regards
Oleg 

14/01/2012
17:45
Avatar
OlegK
Germany
Member
Members
Forum Posts: 1255
Member Since:
10/08/2009
sp_UserOfflineSmall Offline

Hello Tony

I sent the pull request which contains modification of the filterToolbar based on the createSearchingToolbarSynchronization which I posted at the beginning of the thread. The corresponding demo can be used to verify my suggestions.

It's important to mention that only the field which exactly corresponds to the filter operation used in the toolbar will be used. For example if one fills in the Advanced Searching dialog the rule "Client" is equal to "test" the corresponding toolbar field will not be filled because "contains" ("cn") operation are used in the "Client" column.

Best regards
Oleg

P.S. I was not sure in which form is better to send pull requests and posted one more request which consist from the suggested changes plus the changes to forward DOM element of the grid as this in formatter and unformatter.

Forum Timezone: Europe/Sofia

Most Users Ever Online: 376

Currently Online:
27 Guest(s)

Currently Browsing this Page:
1 Guest(s)

Top Posters:

OlegK: 1255

markw65: 179

kobruleht: 144

phicarre: 132

YamilBracho: 124

Renso: 118

Member Stats:

Guest Posters: 447

Members: 11373

Moderators: 2

Admins: 1

Forum Stats:

Groups: 1

Forums: 8

Topics: 10592

Posts: 31289

Newest Members:

razia, Prankie, psky, praveen neelam, greg.valainis@pa-tech.com, swathi

Moderators: tony: 7721, Rumen[Trirand]: 81

Administrators: admin: 66

Comments are closed.
Privacy Policy   Terms and Conditions   Contact Information