📜 ⬆️ ⬇️

Nested Grids with ExtJS 3.0

Introduction



image

The essence of the problem considered in this article lies in the fact that the Grid ExtJS library objects are not intended to be used in the context of nesting. In general, such a task rarely becomes the developer. And yet, sometimes, as, for example, in my case, one has to face it. Below I will try to share the accumulated experience, and, perhaps, I will thus render invaluable help to someone, for which I sincerely hope :). So, good luck ...
')


Nested Grids or ColumnTree



Many may notice that most of the tasks can be simply solved using the ColumnTree method, instead of trying to implement nested “grids”. Yes, if you are completely satisfied with this decision - use it. ColumnTrees are intended for this purpose. But in some cases it is quite difficult to come to terms with the fact that you lose all those useful functions that Grids provide us with, namely: sorting, Drag & Drop columns, filters, etc. etc. If they are really necessary, then you have to think about the implementation of the ability to invest one grid in another. Below is how to do it.

Problems using RowExpander



At first glance, the RowExpander plugin from the ux library is intended to solve the problem. However, not all so simple. This plugin is designed to be able to display / hide arbitrary HTML code in the "grid" line. In particular, when trying to insert another grid into a string, we run into problems with the operation of this plugin. Exit, in fact, 2: write your own plugin or modify the existing one.

I will leave the implementation of my plug-ins to the conscience of those who have a lot of free time, but I chose the path of modification. In the end, no one forbids calling a modified plugin with a different name and using it as something new and new. To health! :)

The main nagging is associated with incorrect display of icons and some related to this, the wrong behavior of the RowExpander plugin, which is inside the “grid” that also uses this plugin. The essence of the idea of ​​solving a problem is that each “grid” nested in another “grid” should use its own style names to determine whether the line is open / closed. Armed with this idea, it is quite simple to realize the dynamic creation of identical styles with different names, directly dependent on the grid ID. Make it pretty simple. Let's change the plugin's constructor a bit by adding the following code:

if (!config.id) {
config.id = Ext.id();
}

Ext.apply( this , config);

var css =
'.x-' + this .id + '-grid3-row-collapsed .x-grid3-row-expander { background-position:0 0; }' +
'.x-' + this .id + '-grid3-row-expanded .x-grid3-row-expander { background-position:-25px 0; }' +
'.x-' + this .id + '-grid3-row-collapsed .x-grid3-row-body { display:none !important; }' +
'.x-' + this .id + '-grid3-row-expanded .x-grid3-row-body { display:block !important; }'
;

Ext.util.CSS.createStyleSheet( css, Ext.id());

this .expanderClass = 'x-grid3-row-expander' ;
this .rowExpandedClass = 'x-' + this .id + '-grid3-row-expanded' ;
this .rowCollapsedClass = 'x-' + this .id + '-grid3-row-collapsed' ;


* This source code was highlighted with Source Code Highlighter .


The need to define the properties of expanderClass, rowExpandedClass and rowCollapsedClass is related to the fact that now it will be quite easy for us to operate with their values ​​inside the plugin, and we still have to make some changes to its code.

Now we have separate style classes for each unique grid on our page. Let's change the methods associated with defining the behavior of the plugin so that they can use the predefined style names:

Change the render method:

return '<div class="' + this .expanderClass + '"> </div>' ;

* This source code was highlighted with Source Code Highlighter .


Also change the toggleRow methods:
this [Ext.fly(row).hasClass( this .rowCollapsedClass) ? 'expandRow' : 'collapseRow' ](row);

* This source code was highlighted with Source Code Highlighter .


expandRow:
Ext.fly( row).replaceClass( this .rowCollapsedClass, this .rowExpandedClass);

* This source code was highlighted with Source Code Highlighter .


and collapseRow:
Ext.fly( row).replaceClass( this .rowExpandedClass, this .rowCollapsedClass);

* This source code was highlighted with Source Code Highlighter .


Everything, on this treatment of the plug-in disease described above can be considered complete.

However, a complete solution to all problems with this does not come.

Tree branches and leaves



First of all, like any other tree, ours should be able to correctly reflect the “branches” and “leaves”. By these concepts, I mean that the "branch" can be opened (it has descendants), and the leaves have no descendants. Unfortunately, in its current state, RowExpander cannot distinguish “leaves” from “branches” and adds open / close controls to all rows of the “grid”. For the full implementation of the tree, we will have to continue to change it.

Fortunately, the decision is not so difficult. Let's dwell on the thesis that our data set should contain a sign of whether the record is finite (“sheet”) or may contain descendants (“branch”). To do this, it is enough to define a field in the record, for example, with the name “is_leaf”, taking the logical value true or false (is_leaf = true - “sheet”, is_leaf = false - “branch”).

At the same time, we should not prevent the plugin from working as it did before. That is, we will determine for ourselves how and when the plugin should work in tree mode, by default, it will behave as before.

To implement this mechanism, we define the public configuration properties actAsTree = false and treeLeafProperty = 'is_leaf'. Thus, when initializing a plug-in, it will be possible to indicate whether the plug-in should behave like a tree (in fact, check whether the “grid” lines are leaves of the tree), and also set the name of the “sheet” attribute in the record.

First of all, we need to define another style to display the sheet (in fact, it should just hide the open / close element on elements with type is_leaf = true):

Add to the already created style definition:
var css =
...
+ '.x-grid-expander-leaf .x-grid3-row-expander { background: none; }'
;


* This source code was highlighted with Source Code Highlighter .


and define a property that we could use inside the plugin for the given style class name:
this .leafClass = 'x-grid-expander-leaf' ;

* This source code was highlighted with Source Code Highlighter .


There is only a handful of things left; you need to affix this class to the appropriate lines and prohibit actions to open / close lines marked as leaves.

To do this, change the getRowClass method (row initialization):
var cssClass = this .state[record.id] ? this .rowExpandedClass : this .rowCollapsedClass;
if ( this .actAsTree && record.get( this .treeLeafProperty)) {
cssClass = this .leafClass;
}
return cssClass;


* This source code was highlighted with Source Code Highlighter .


and add a check to the toggleRow, expandRow, collapseRow methods (prohibit actions on the leaves):
if (Ext.fly(row).hasClass( this .leafClass)) {
return ;
}


* This source code was highlighted with Source Code Highlighter .


Event handling problem



Even after all this, problems remain. The behavior of “grids” nested in each other turns out to be rather inadequate. You will encounter glitches when selecting rows, sorting, etc. Actions on the rows of descendants will be projected on the parent "grid". This is all pretty unpleasant, but very easily solved!

On each newly created descendant just need to disable pop-up events. To do this, we will make the next change in RowExpander to the onRender method, which in turn is executed at the time of rendering the “grid” itself.

if ( this .actAsTree) {
grid.getEl().swallowEvent([ 'mouseover' , 'mouseout' , 'mousedown' , 'click' , 'dblclick' ]);
});


* This source code was highlighted with Source Code Highlighter .


Problem with memory leaks



And not all problems are solved :). We still need to take care of removing all the components associated with the collapsed grids. It makes no sense to describe the detailed changes, especially since this area may still change more than once. Just look at the code:

The full code of the modified RowExpander plugin: RowExpander.js
A live example of nested grids: ExtJS Nested Grids Example

Conclusion



Despite the primary difficulties in building nested grids, and seemingly confronted with the unsuitability of “grids” to nesting, in a rather simple way, we managed to achieve quite an acceptable result, which once again shows the flexibility and extensibility of the ExtJS library.

It only remains for me to hope that this article will help someone to overcome the difficulties.

Good luck in development!

PS This is a cross-posting of the original article " Nested Grids using ExtJS 3.0 " posted on my personal blog.

Source: https://habr.com/ru/post/78023/


All Articles