我最初是在Sencha论坛here上发布的,但没有得到任何回复(除了我自己的答案,我将很快发布),所以我将在这里重新发布,看看我是否得到再帮助了。
我一直在讨论如何在4.0.7中过滤TreeStore。我尝试了以下内容:
模型
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'}
});
商店
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'
}
}
});
树
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
});
过滤器
var filter = Ext.create('Ext.util.Filter', {
filterFn: function(item) {
return item.data.text == 'leaf1';
}
});
所以我认为我的问题是......我不知道如何使用这个过滤器,因为TreeStore实际上没有继承任何类型的过滤器函数,如普通商店。我试过了:
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.
通常情况下,如果我有一个网格并且我创建了一个像上面那样的过滤器,我可以做myTree.store.filter(filter)
并且它会抓住我返回的每一行的项目/过滤器...但我在想,因为TreeStore不会继承一个未被传入的过滤函数。
如果有人能够清楚地说明我做错了什么,或者对如何设置过滤功能/思考过程有任何见解,请继续。我很感激任何帮助。
答案 0 :(得分:6)
感谢您抓住other one,我修复了答案,以包含下面包含的更具动态的树存储过滤器覆盖,以回答您的问题。
它在4.1b2中工作正常,我知道4.07和4.1之间的树库有一些变化,但我认为4.07仍然有我在这里使用的树对象。
这里是覆盖:
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;
}
});
它使用store.tree.nodeHash
对象来迭代所有节点而不是第一个子节点。它将接受过滤器作为函数或属性/值对。我认为clearFilter方法可以解决,以防止另一个ajax调用。
答案 1 :(得分:1)
这是我想出的答案......它并不理想,所以我希望有人可以提供更好,更通用的方法。为什么?好吧,如果我的树上有一个孩子有一个孩子的父母,我想过滤那些,但我的解决方案只有一个孩子深。
感谢this thread,我想出了一些事情。这个线程的唯一问题是它使得过滤平面...所以子节点不会出现在它们的父节点下。我修改了他们的实现并想出了这个(它只有1个孩子深,所以如果你的父母包含一个有孩子的孩子,那就不行了):
<强> 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;
}
所以当我做这样的事情时(在第一篇文章中使用我的树),这是有效的:
Ext.getCmp('myTree').store.filterBy(function(rec) {
return rec.data.id != 'child1';
});
此代码将返回每个没有child1 id的记录,因此在leaf1下,它只会将child2作为节点。我也可以通过Ext.getCmp('myTree').store.clearFilter()
清除过滤器。
现在,我意识到我刚刚回答了我自己的问题,但就像我上面发布的那样,我真的很喜欢批评/建议我可以提高效率和通用性。如果有人有任何提示,我很乐意听到他们!另外,如果您需要帮助来获取并运行此代码,请告诉我们。
沙,我也试过过滤器,但没有运气。看看this thread。
答案 2 :(得分:0)
我一直在寻找一种过滤树库的方法,这样如果filterBy函数为任何节点返回true,我想显示该节点的完整节点层次结构,包括所有父节点,祖父节点等和子节点,大子节点等我从这个问题中提供的其他解决方案中修改了它。此解决方案以递归方式工作,因此treestore可以是任何大小。
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;
}
});
所以它就像普通商店filterBy调用一样。
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;
});
答案 3 :(得分:0)
上面的覆盖是很好的,它解决了我的一些问题,但是,我发现了一个很难用上面的代码找到的bug。花了半天后,我发现我们需要使用slice()来复制数组,否则会删除一些节点。
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;
}
});
答案 4 :(得分:0)
我可以使用 onbeforeappend 事件进行一些基本过滤。 虽然结构不如上述解决方案,但它提供了一种简单直接的方法来应用基本过滤,而无需覆盖基类方法或使用外部插件。
我在商店本身实现了过滤。 在更高级的场景中,这也可以在控制器中完成。
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;
}
}
});