首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > JavaScript >

ExtJS兑现异步Tree的节点搜索

2012-09-04 
ExtJS实现异步Tree的节点搜索????? 前言:在任何一个Tree树中,提供查找功能无疑会大大方便用户。不用睁大眼

ExtJS实现异步Tree的节点搜索

????? 前言:在任何一个Tree树中,提供查找功能无疑会大大方便用户。不用睁大眼睛一级一级去展开,只要输入关键字,回车就能自动定位到节点,岂不快哉?这样的用户体验是相当完美的。但在动态异步加载的Tree树中,客户端实现这样的功能就有点困难,因为节点是异步动态加载的。默认是没有全部从服务器端取回的,通常的做法是默认加载第一级,其他级的节点都是惰性按需加载的,用户点了才会展开。而对于这个没有完全加载的树,用户希望搜索节点,怎么实现?笨办法是先展开树的所有节点,然后再在树中搜索。这样的话在服务器数据量大的情况下会非常慢。所以在数据量大的情况下,是不采取这种实现方式的,这里的实现方法是在服务器端的Servlet中查找,通过AJAX返回第一个匹配节点的路径Path,然后展开这个路径,选中这个搜索到节点。


效果图:

ExtJS兑现异步Tree的节点搜索

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

???????? 一、先展开树的所有节点,然后再在树中搜索

?

Ext.onReady(function() {            Ext.QuickTips.init();            Ext.BLANK_IMAGE_URL = "../../resources/images/default/s.gif";            var mytree = new Ext.tree.TreePanel({region: 'center',title : "简单Extjs动态树",                //el : "container",                animate : true, //展开,收缩动画,false时,则没有动画效果                    collapsible : true,                enableDD : true,//不仅可以拖动,还可以通过Drag改变节点的层次结构(drap和drop)                enableDrag : true,//仅仅drop                 rootVisible : true,//false不显示根节点,默认为true                 autoScroll : true,                autoHeight : true,                width : 150,//tbar:new Ext.Toolbar(),tbar:[' ',new Ext.form.TextField({width:150,emptyText:'快速检索',enableKeyEvents: true,listeners:{keyup:function(node, event) {findByKeyWordFiler(node, event);},scope: this}})],root:new Ext.tree.AsyncTreeNode({id:"root",                      text:"树的根",                      leaf:false, //expanded:true,children: [{id: 'level',text: '用户类型',children: [{id: 'allLevel',text: '全部',leaf: true}, {id: 'noSupport',text: '无支持',leaf: true}, {id: 'month',text: '包月',leaf: true}, {id: 'year',text: '包年',leaf: true}, {id: 'always',text: '终身',leaf: true}]}, {id: 'outOfDate',text: '是否过期',children: [{id: 'allOutOfDate',text: '全部',leaf: true}, {id: 'notOutOfDate',text: '未过期',leaf: true}, {id: 'alreadyOutOfDate',text: '已过期',leaf: true}]}, {id: 'report',text: '统计图表',children: [{id: 'levelReport',text: '按用户类型',leaf: true}, {id: 'outOfDateReport',text: '按是否过期',leaf: true}]}]            }),                lines : true//节点间的虚线条            });//mytree.expandAll();                        //mytree.render();/*var filterTreeFiled = new Ext.form.TextField({width:150,emptyText:'快速检索',enableKeyEvents: true});var tbar = mytree.getTopToolbar();tbar.add(filterTreeFiled);tbar.doLayout();*/var timeOutId = null;var treeFilter = new Ext.tree.TreeFilter(mytree, {clearBlank : true,autoClear : true});// 保存上次隐藏的空节点var hiddenPkgs = [];var findByKeyWordFiler = function(node, event) {clearTimeout(timeOutId);// 清除timeOutIdmytree.expandAll();// 展开树节点// 为了避免重复的访问后台,给服务器造成的压力,采用timeOutId进行控制,如果采用treeFilter也可以造成重复的keyuptimeOutId = setTimeout(function() {// 获取输入框的值var text = node.getValue();// 根据输入制作一个正则表达式,'i'代表不区分大小写var re = new RegExp(Ext.escapeRe(text), 'i');// 先要显示上次隐藏掉的节点Ext.each(hiddenPkgs, function(n) {n.ui.show();});hiddenPkgs = [];if (text != "") {treeFilter.filterBy(function(n) {// 只过滤叶子节点,这样省去枝干被过滤的时候,底下的叶子都无法显示return !n.isLeaf() || re.test(n.text);});// 如果这个节点不是叶子,而且下面没有子节点,就应该隐藏掉mytree.root.cascade(function(n) {if(n.id!='0'){if(!n.isLeaf() &&judge(n,re)==false&& !re.test(n.text)){hiddenPkgs.push(n);n.ui.hide();}}});} else {treeFilter.clear();return;}}, 500);}// 过滤不匹配的非叶子节点或者是叶子节点var judge =function(n,re){var str=false;n.cascade(function(n1){if(n1.isLeaf()){if(re.test(n1.text)){ str=true;return; }} else {if(re.test(n1.text)){ str=true;return; }}});return str;};// 给输入框绑定keyup事件,需要加上enableKeyEvents:true才能让extjs的textfield代理鼠标事件//filterTreeFiled.on("keyup", function(node, event) {//findByKeyWordFiler(node, event);//});var eventPanel = new Ext.Panel({width  : 680,height : 350,renderTo : 'container',layout: 'border',items:[mytree]  });                    });

?

注意ext-base.js和ext-all.js的引用顺序,具体的可以查看INCLUDE_ORDER.txt

?

<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" /> <script type="text/javascript" src="../../adapter/ext/ext-base.js"></script><script type="text/javascript" src="../../ext-all.js"></script>        <script type="text/javascript" src="treenode.js"></script>     <body>        <div id="container"></div>    </body>
?

??? 二、 通过AJAX返回第一个匹配节点的路径Path,然后展开这个路径,选中这个搜索到节点

?

要实现此功能需解决三个问题:

1.? 通过Ajax将节点id或text传回服务器。

2.? 在服务器端查找到节点path,path由节点id号和“/”构成。注意path的格式,如:'/0/4/7'。此处0表示根节点id,7表示被查找的节点id。

3. 将path传回客服端,Extjs将通过Ext.tree.TreePanel的expandPath方法展开节点。

?

在ExtJS中,AsyncTreeNode是异步节点,TreeLoader实现对树结点的异步加载,即使服务器取到大量的数据,也没有问题,异步加载能保证性能和节点的按需加载。服务端需要生成指定格式的Json字符串。

?

var eventTree = new Ext.tree.TreePanel({          region: 'center',          collapsible: false,          title: '导航',          xtype: 'treepanel',          id:'event-west-tree',          width: 180,          animate:false, //展开,收缩动画          autoScroll: true,          enableDD:true,          split: true,          loader: new Ext.tree.TreeLoader({            dataUrl:'ruleGroupTree.do?json=1'          }),          root: new Ext.tree.AsyncTreeNode({            text: '分类规则组',            draggable:false,            //expanded:true, //默认展开第一级            id:'0'          }),          tbar:[{            iconCls: 'icon-expand-all',tooltip: '展开',            handler: function(){ eventTree.expandAll(); },            scope: this          }, '-', {            iconCls: 'icon-collapse-all',            tooltip: '收缩',            handler: function(){ eventTree.collapseAll(); },            scope: this          }, new Ext.form.TextField({width: 115,emptyText:'快速检索',                enableKeyEvents: true,//给输入框绑定keyup事件,需要加上enableKeyEvents:true才能让extjs的textfield代理鼠标事件listeners:{                    keyup:function(node, event) {findByKeyWordFiler(node, event);},                    scope: this}})]});eventTree.expandAll();var filterTreeFiled = new Ext.form.TextField({width:115,    emptyText:'快速检索',    enableKeyEvents: true});var tbar = eventTree.getTopToolbar();tbar.add(filterTreeFiled);tbar.doLayout();var selectNode = function(node) {    node.ensureVisible();    node.select();    node.fireEvent('click', node);}function onExpandPathComplete(bSuccess, oLastNode) {    if (!bSuccess)        return;    // focus 节点,并选中节点!    selectNode(oLastNode);}var findByKeyWordPath = function(node, event) {    clearTimeout(timeOutId);    timeOutId = setTimeout(function() {                var text = node.getValue().trim();                // 采用ajax获得需要展开的路径                if (text != "") {                    Ext.Ajax.request({                    params : {                                    keyWord : text                                },                                url : 'ruleGroupTree.do?json=1',                                method : 'POST',                                async : false,                                success : function(response, opts) {                                    var obj = Ext.decode(response.responseText);                                    eventTree.expandPath('/0/101/10101','id',onExpandPathComplete);                                    eventTree.expandPath('/0/101/10101', 'id', function(bSucess,oLastNode){          eventTree.getSelectionModel().select(oLastNode);        });                                    if(obj.success){                                    var length = obj.length;                                    eventTree.root.reload();                                    //eventTree.expandAll();                                    for (var i = 0; i < length; i++) {                                        var path = obj[i].path;                                        eventTree.expandPath('/0/101/275','id',onExpandPathComplete);                                    }                                },                                failure : function(response, opts) {                                    Ext.Msg.alert("错误提示", "请求失败,请与开发人员联系。").setIcon(Ext.MessageBox.ERROR);                                }                            });                } else {                }            }, 500);}filterTreeFiled.on("keyup", function(node, event) {findByKeyWordPath(node, event);});
?

?

以下是具体实现:

1. servlet端要实现的功能就是封装path,将path发送到客服端即可,格式如上。代码省略。

2. 客户端代码:

// 查找树节点
?searchNode : function() {
????? var searchForm = Ext.getCmp("searchForm").getForm();
????? var param = searchForm.getValues();
???? ?if(searchForm.isValid()){
????????? Ext.Ajax.request({
????????????? ?url: 'dept!search.action',
?????????????? params:{formData:Ext.encode(param)},
?????????????? success:function(response){
?????????????????? var o = Ext.decode(response.responseText);
????????????????? ?if(o.success){
?????????????????????? var tree = Ext.getCmp('sysOrgs');
????????????????????? ?path=o.message;
????????????????????? ?tree.expandPath(path, 'id', this.onExpandPathComplete);
?????????????????? }
???????????????},
?????????????? failure:function(response){
?????????????? },
?????????????? scope:this
???????? });
????? }
?},
?onExpandPathComplete:function(bSuccess, oLastNode) {
??if(!bSuccess)
???return;
??//focus 节点,并选中节点!,以下代码不可少?
??oLastNode.ensureVisible();
??oLastNode.select();
??oLastNode.fireEvent('click', oLastNode);
??
?}

关于tree.expandPath方法的使用具体参照API文档。

?

1 楼 1v1_问天 2012-05-18   如果客户端返回的path有多个怎么办?后一个path展开就会覆盖前一个path的展开,这个问题怎么解决。

热点排行