Search code examples
extjstreeextjs4store

Ext JS 4: Filtering a TreeStore


I originally posted this on the Sencha forums here but didn't get any responses (other than my own answer, which I will post soon), so I am going to repost it here and see if I get anymore help.

I've been racking my brain on how to filter a TreeStore in 4.0.7. I've tried the following:

The 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'}
});

The store

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'
    }
  }
});

The 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
});

The filter

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

So I think my problem is... I don't know how to use this filter due to TreeStore not actually inheriting any type of filter functions like a normal store. I've 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.

Normally, if I have a grid and I create a filter like above, I can just do myTree.store.filter(filter) and it'll grab each row's item/filter on what I return... but I'm thinking because TreeStore doesn't inherit a filtering function, that's not being passed in.

If someone could provide some clarity as to what I'm doing wrong or any insight on how to set up a filter function/my thinking process, please go ahead. I'd appreciate any help.


Solution

  • Thanks for catching that other one, I fixed up the answer to include the more dynamic treestore filter override that I included below to answer your Q.

    It is working fine in 4.1b2, I know there were some changes to the treestore between 4.07 and 4.1 but I think 4.07 still had the tree objects I am using here.

    Here's the override:

    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 rather than just the first child. It will accept a filter as a function or property/value pair. I suppose the clearFilter method could be worked over though to prevent another ajax call.