Touch中,定义了一个通讯录的面板,面板中包含了一个 nestedList,同时还定义了一个 TreeStore,通过ajax的方式加载远程数据并显示,代码片段如下:
// 定义通讯录面板
Ext.define('MyApp.view.AddressPanel', {
    extend: 'Ext.Panel',
    config: {
        itemId: 'addressPanel',
        items: [
            {
                xtype: 'nestedlist',
                store: 'AddressJsonTreeStore',
                title: '通讯录',
                ...
            }, ...
        ], ...
    }, ...
};
// 定义 TreeStore
Ext.define('MyApp.store.AddressJsonTreeStore', {
    extend: 'Ext.data.TreeStore',
    config: {
        model: 'MyApp.model.AddressModel',
        storeId: 'AddressJsonTreeStore',
        proxy: {
            type: 'ajax',
            url: 'address',
            reader: {
                type: 'json'
            }
        }, ...
    }, ...
});
现在,需要在这个面板的title bar上,添加一个查询框,以实现根据名称查询的功能,由于数据库比较大,并且都是延迟加载,因此仍然采用ajax的加载方式。
首先,我们在通讯录面板的title bar上添加一个查询框searchfield:
...
title: '通讯录',
toolbar: {
    xtype: 'titlebar',
    items: [
        {
            xtype: 'searchfield',
            align: 'right',
            itemId: 'seaerchAddressField',
            maxWidth: '130px',
            label: '',
            placeHolder: ' 姓名..'
        },...
    ]
}
添加了查询框后,界面如下图所示:

然后,修改控制器,添加查询和重置事件:
Ext.define('MyApp.controller.AddressController', {
    extend: 'Ext.app.Controller',
    config: {
        refs: {
            // 定义一个属性,便于使用
            addressNestedList: 'addresspanel nestedlist[itemId=addressNestedList]',
            ...
        },
        control: {
            // 定义两个事件
            "addresspanel [itemId=seaerchAddressField]": {
                action: 'onSeaerchAddressFieldAction',    // 点击查询后的事件
                clearicontap: 'onSeaerchAddressFieldClearicontap' // 点击重置图标后的事件
            }, ...
        }
    },
    
    /**
     * 点击查询后的事件
     */
    onSeaerchAddressFieldAction: function(textfield, e, eOpts) {
        this.searchAction( textfield.getValue().trim() );
    },
    /**
     * 点击重置图标后的事件
     */
    onSeaerchAddressFieldClearicontap: function(textfield, e, eOpts) {
        this.searchAction();
    },
    /**
     * 执行查询或重置
     */
    searchAction: function(val) {
        ......
    }
});
上面的控制器代码中,关键就是下面那个 searchAction 方法,这里将执行查询操作:
- 方案1:TreeStore 有一个 remoteFilter 属性,设置为 true 后,可以调用其 filter 或 find 方法执行ajax查询。
 但是在实际测试中,这种方法的性能非常低下,基本不可用
- 方案2:每次查询时,重新创建一个新的 store 对象,并设置为 nestedList 的 store,执行ajax查询与重新渲染。
 这中方案执行效率相当高,最终的代码示例如下。
searchAction: function(val) {
    if ( val ) {
        val = val.replace(/['"\/\\\s]/g, '');
        if ( val.length < 2 ) {
            return Ext.Msg.alert("请输入至少两个字");
        }
    }
    var list = this.getAddressNestedList();
    var storeId = "AddressJsonTreeStore_" + val;
    if ( !val ) { // 重置
        store = Ext.getStore("AddressJsonTreeStore");
    } else {      // 搜索
        store = Ext.getStore(storeId);
        if ( !store ) {
            // 不同的查询条件,后台返回的机构id必须不一样,否则可能显示后台数据中没有的机构节点
            store = Ext.create("MyApp.store.AddressJsonTreeStore", { storeId: storeId });
            store.getProxy().setExtraParam('find', val);  // 设置远程查询参数
            list.getStore().setAutoDestroy(true);
            list.setEmptyText("没有找到您想要的数据!");
        }
    }
    list.updateStore( store );    // 速度最快
    list.setStore( store );
}
在上面的最终代码中,需要注意后台返回的查询结果 json 数据中,如果仍然为树形结构,那么每个节点(非叶子节点)的id建议保持不一样,例如:
- 不是查询时,有如下的结构:{ data:{id:'foo', name:'foobar', data:{...}}, ... }
- 则查询结果应为:{ data:{id:'bar', name:'foobar', data:{...}}, ... }
原因在于 nestedList 的渲染缓存,如:
- 点开节点foobar,可以看到子节点 foo 与 bar
- 查询后,如果返回结果中的节点foobar的id保持不变,且子节点只有foo了,但此时点开节点foobar,仍然会显示出节点 foo 与 bar,这显然是错误的
- 若查询的返回结果中的节点foobar的id设置为一个新的值,则点开节点foobar后,只会显示出一个foo子节点,这才是正确结果。