Ext JS 4: Filtering TreeStore

I originally posted this on the Sencha forums here , but have not received any answers (other than my own answer, which I will post shortly), so I am going to post it here and see if I get any more help.

I was trying to figure out how to filter a TreeStore in 4.0.7. I tried the following:

Model

Ext.define('model', { extend: 'Ext.data.Model', fields: [ {name: 'text', type: 'string'}, {name: 'leaf', type: 'bool'}, {name: 'expanded', type: 'bool'}, {name: 'id', type: 'string'} ], hasMany: {model: 'model', name: 'children'} }); 

Score

 Ext.define('myStore', { extend: 'Ext.data.TreeStore', model: 'model', storeId: 'treestore', root: { text: 'root', children: [{ text: 'leaf1', id: 'leaf1', children: [{ text: 'child1', id: 'child1', leaf: true },{ text: 'child2', id: 'child2', leaf: true }] },{ text: 'leaf2', id: 'leaf2', leaf: true }] }, proxy: { type: 'memory', reader: { type: 'json' } } }); 

Tree

 var myTree = Ext.create('Ext.tree.Panel', { id: 'myTree', selType: 'cellmodel', selModel: Ext.create('Ext.selection.CellModel', {mode: 'MULTI'}), rootVisible: false, store: Ext.create('myStore'), width: 300 }); 

Filter

 var filter = Ext.create('Ext.util.Filter', { filterFn: function(item) { return item.data.text == 'leaf1'; } }); 

So, I think that my problem ... I do not know how to use this filter because TreeStore does not actually inherit any filter functions, such as a regular store. I tried:

 myTree.store.filters.add(filter); myTree.store.filters.filter(filter); // This seems to work // I can get into the filterFn when debugging, but I think item is the "this" of my filter object. 

Usually, if I have a grid and I create a filter as above, I can just do myTree.store.filter(filter) , and it will capture every element / filter of the row on what I return ... but I guess because TreeStore doesn't inherit a filter function that is not passed.

If someone can give some clarity about what I'm doing wrong, or clarify how to set up the filter function / my thinking process, please continue. I would appreciate any help.

+6
source share
5 answers

Thank you for catching this other one , I fixed the answer by including in it a more dynamic treestore filter filter, which I included below to answer your Q question.

It works fine in 4.1b2, I know that there have been some changes in the treestore between 4.07 and 4.1, but I think that 4.07 still had the tree objects that I use here.

Here's the redefinition:

 Ext.override(Ext.data.TreeStore, { hasFilter: false, filter: function(filters, value) { if (Ext.isString(filters)) { filters = { property: filters, value: value }; } var me = this, decoded = me.decodeFilters(filters), i = 0, length = decoded.length; for (; i < length; i++) { me.filters.replace(decoded[i]); } Ext.Array.each(me.filters.items, function(filter) { Ext.Object.each(me.tree.nodeHash, function(key, node) { if (filter.filterFn) { if (!filter.filterFn(node)) node.remove(); } else { if (node.data[filter.property] != filter.value) node.remove(); } }); }); me.hasFilter = true; console.log(me); }, clearFilter: function() { var me = this; me.filters.clear(); me.hasFilter = false; me.load(); }, isFiltered: function() { return this.hasFilter; } }); 

It uses the store.tree.nodeHash object to iterate through all nodes against the filters, and not just for the first child. It will take a filter as a function or a property / value pair. I believe that the clearFilter method can be handled though to prevent another ajax call.

+6
source

This is the answer I came up with ... it is not perfect, so I hope someone can provide a better, more general approach. What for? Well, if my tree had a parent who had a child, who had a child, I would like to filter it, but my decision is only for one child.

Thanks to this stream , I understood some things. The only problem with this thread is that it made filtering flat ... therefore, the child nodes did not appear under their parent nodes. I changed their implementation and came up with this (it only goes 1 child in depth, so it will not work if you have a parent that contains a child that has a child):

Treestore

 filterBy : function(fn, scope) { var me = this, root = me.getRootNode(), tmp; // the snapshot holds a copy of the current unfiltered tree me.snapshot = me.snapshot || root.copy(null, true); var hash = {}; tmp = root.copy(null, true); tmp.cascadeBy(function(node) { if (fn.call(me, node)) { if (node.data.parentId == 'root') { hash[node.data.id] = node.copy(null, true); hash[node.data.id].childNodes = []; } else if (hash[node.data.parentId]) { hash[node.data.parentId].appendChild(node.data); } } /* original code from mentioned thread if (fn.call(scope || me, node)) { node.childNodes = []; // flat structure but with folder icon nodes.push(node); }*/ }); delete tmp; root.removeAll(); var par = ''; for (par in hash) { root.appendChild(hash[par]); } return me; }, clearFilter: function() { var me = this; if (me.isFiltered()) { var tmp = []; var i; for (i = 0; i < me.snapshot.childNodes.length; i++) { tmp.push(me.snapshot.childNodes[i].copy(null, true)); } me.getRootNode().removeAll(); me.getRootNode().appendChild(tmp); delete me.snapshot; } return me; }, isFiltered : function() { return !!this.snapshot; } 

So this works when I do something like this (using my tree in the first post):

 Ext.getCmp('myTree').store.filterBy(function(rec) { return rec.data.id != 'child1'; }); 

This code will return every record that does not have the identifier child1, so under leaf1 it will only have child2 as a node. I can also clear the filter by doing Ext.getCmp('myTree').store.clearFilter() .

Now I understand that I just answered my question, but, as I wrote above, I would really like to criticize / consult on what I can do more efficient and universal. If anyone has any advice, I would love to hear them! Also, if you need help running this code, let me know.

Shah, I also tried filters, but no luck. Check out this thread .

+1
source

I was looking for a way to filter the treestore so that if the filterBy function came back for any node, I wanted to display the full node hierarchy of this node, including all the parent nodes, grand parent node, etc. and child nodes, grand child node, etc. I changed it from the other solutions provided in this question. These solutions work recursively, so the treestore can be of any size.

 Ext.override(Ext.data.TreeStore, { hasFilter: false, /** * Filters the current tree by a function fn * if the function returns true the node will be in the filtered tree * a filtered tree has also a flat structure without folders */ filterBy : function(fn, scope) { var me = this, nodes = [], root = me.getRootNode(), tmp; // the snapshot holds a copy of the current unfiltered tree me.snapshot = me.snapshot || root.copy(null, true); tmp = me.snapshot.copy(null, true); var childNodes = tmp.childNodes; root.removeAll(); for( var i=0; i < childNodes.length; i++ ) { //Recursively tranverse through the root and adds the childNodes[i] if fn returns true if( this.traverseNode( childNodes[i], root, fn ) == true ) { i--; } } return me; }, /** * Recursively tranverse through the root and adds the childNodes[i] if fn returns true */ traverseNode: function( node, parentNode, fn ) { var me = this; if( fn.call( me, node ) ) { parentNode.appendChild( node ); return true; } if( node.hasChildNodes() ) { var childNodes = node.childNodes; var found = false; for( var i=0; i < childNodes.length; i++ ) { if( this.traverseNode( childNodes[i], node, fn ) == true ) { found = true; } } if( found == true ) { parentNode.appendChild( node ); return true; } } return false; }, /** * Clears all filters a shows the unfiltered tree */ clearFilter : function() { var me = this; if (me.isFiltered()) { me.setRootNode(me.snapshot); delete me.snapshot; } return me; }, /** * Returns true if the tree is filtered */ isFiltered : function() { return !!this.snapshot; } }); 

Thus, it works just like a regular storeBy filter.

 searchText = "searchText"; store.filterBy( function(item) { var keys = item.fields.keys; for( var i=0; i < keys.length; i++ ) { var value = item.get( keys[i] ); if( value != null ) { if( value.toString().toLowerCase().indexOf( searchText ) !== -1 ) { return true; } } } return false; }); 
0
source

The above redefinition is great and it solves some of my problems, however I found an error that is hard to find with the above code. After spending half a day, I realized that we need to use slice () to copy the array, otherwise some nodes will be deleted.

  Ext.override(Ext.data.TreeStore, { hasFilter: false, /** * Filters the current tree by a function fn * if the function returns true the node will be in the filtered tree * a filtered tree has also a flat structure without folders */ filterBy: function (fn, scope) { var me = this, nodes = [], root = me.getRootNode(), tmp; // the snapshot holds a copy of the current unfiltered tree me.snapshot = me.snapshot || root.copy(null, true); tmp = me.snapshot.copy(null, true); var childNodes = tmp.childNodes.slice(); root.removeAll(); for (var i = 0; i < childNodes.length; i++) { //Recursively tranverse through the root and adds the childNodes[i] if fn returns true this.traverseNode(childNodes[i], root, fn); } return me; }, /** * Recursively tranverse through the root and adds the childNodes[i] if fn returns true */ traverseNode: function (node, parentNode, fn) { var me = this; if (fn.call(me, node)) { parentNode.appendChild(node); return true; } if (node.hasChildNodes()) { var t_childNodes = node.childNodes.slice(); var found = false; for (var i = 0; i < t_childNodes.length; i++) { if (this.traverseNode(t_childNodes[i], node, fn) == true) { found = true; } } if (found == true) { parentNode.appendChild(node); return true; } } return false; }, /** * Clears all filters a shows the unfiltered tree */ clearFilter: function () { var me = this; if (me.isFiltered()) { me.setRootNode(me.snapshot); delete me.snapshot; } return me; }, /** * Returns true if the tree is filtered */ isFiltered: function () { return !!this.snapshot; } }); 
0
source

I was able to perform basic filtering using the onbeforeappend event. Although it is not as well structured as the above solutions, it provides a simple and direct way to apply basic filtering without having to redefine the base class methods or use external plugins.

I implemented my filtering in the store itself. In more complex scenarios, this can also be done in the controller.

 Ext.define('MyApp.store.FilteredTreeStore', { extend: 'Ext.data.TreeStore', .... .... listeners: { beforeappend: function (thisStore, node, eOpts) { var allowAppend = false; allowAppend = --your filtering logic here --returning false will cancel append of the entire sub tree return allowAppend; } } }); 
0
source

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


All Articles