使用Dojo的FilteringSelect打造具有拼音检索功能的下拉菜单(上)

时间:2022-09-23 15:04:22

感谢王牌海盗的投稿!本文首发于:http://cosbor.web-144.com/?p=38 

=======================================================================================
在我们国内开发应用系统的过程中往往会遇到这样的需求:下拉菜单中的条目过多时,用户在筛选时往往非常费劲,希望能提供条目拼音简码的方式进行筛选,加快选择速度。而Dojo的FilteringSelect是个非常优秀的具有动态筛选及autoComplete的下拉菜单组件,我们尝试用它来实现一个具有拼音检索功能的下拉菜单。我们以一个三国人物选择的下拉菜单为例看看FilteringSelect的使用,注意demo使用的是dojo的1.8版本。

?
FilteringSelect.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
< html >
< head >
     < title >下拉菜单</ title >
     
< link rel = "stylesheet" href = "static/js/dojo/dojo/resources/dojo.css" />
< link rel = "stylesheet" href = "static/js/dojo/dijit/themes/claro/claro.css" />
< script type = "text/javascript" src = "static/js/dojo/dojo/dojo.js" data-dojo-config = "async: true,parseOnLoad: true" ></ script >
 
< script type = "text/javascript" >
     require([
         'dijit/form/FilteringSelect',
         'dojo/store/Memory',
         'dojo/domReady!'
     ],function(FilteringSelect,Memory){
             
           //下拉菜单的store,"py"字段存放每个条目的拼音简码
           var selectStore = new Memory({
                 data:[
                     {name:'赵云',id:'1',py:'zy'},    
                     {name:'张飞',id:'2',py:'zf'},    
                     {name:'刘备',id:'3',py:'lb'},    
                     {name:'关羽',id:'4',py:'gy'},    
                     {name:'黄忠',id:'5',py:'hz'},    
                     {name:'魏延',id:'6',py:'wy'},    
                     {name:'周瑜',id:'7',py:'zy'},    
                     {name:'孙坚',id:'8',py:'sj'},    
                     {name:'曹操',id:'9',py:'cc'},    
                     {name:'夏侯敦',id:'10',py:'xhd'},    
                 ]
           });
           
           //创建FilteringSelect
           var testSelect = new FilteringSelect({
               id: "testSelect",
               name: "test",
               value: "",
               store: selectStore,
               searchAttr: 'py',//指定输入文本框进行用来进行检索的字段
               labelAttr: 'name',//指定下拉菜单中显示的字段
               required:false,
               autoComplete:false
           },"testSelect");
         
     });
</ script >
</ head >
 
< body  class = "claro" >
< div style = "text-align: center;width: 100%;padding-top: 100px;font-size:15px;" >
         三国人物:< input id = "testSelect" />
</ div >
</ body >
</ html >

运行页面,在下拉菜单输入框中输入“zy”,发现下拉菜单已经可以根据输入的拼音简码进行过滤了,如下图:
使用Dojo的FilteringSelect打造具有拼音检索功能的下拉菜单(上)

使用Dojo的FilteringSelect打造具有拼音检索功能的下拉菜单(上)

这时,问题来了,你会发现当鼠标选定下拉菜单中条目,如选“周瑜”时,最终在输入框中呈现的并不是我们想要的中文名称“周瑜”而是该条目对应的py字段的值“zy”。这是因为FilteringSelect会以searchAttr属性中设置的字段作为最终显示结果。但这并不是我们想要的结果,我们希望显示的是中文的name字段。
使用Dojo的FilteringSelect打造具有拼音检索功能的下拉菜单(上)

经过翻看FilteringSelect的源码,发现可以对FilteringSelect进行一下小的改造来满足我们的要求。可以使用Dojo提供的自定义组件机制,通过继承FilteringSelect来创建一个满足我们需求的FilteringSelect组件。代码如下:

?
customSelect
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
define([
     "dojo/_base/declare" , // declare,
     "dojo/dom-attr" , // domAttr.get
     "dijit/form/FilteringSelect"
], function (declare, domAttr ,FilteringSelect){
 
     return declare( "test.FilteringSelect" , [FilteringSelect], {
            
              displayValueAttr: null , //新增一个自定义属性,用于指定FilteringSelect的textbox中最终显示内容的属性字段
         
             // summary:
             // 覆盖dijit.form._AutoCompleterMixin的同名方法,使FilteringSelect支持displayValueAttr指定textbox最终显示内容,而不是默认显示searchAttr指定的字段内容
             _announceOption: function ( /*Node*/ node){
 
                 if (!node){
                     return ;
                 }
                 // pull the text value from the item attached to the DOM node
                 var newValue;
                 if (node == this .dropDown.nextButton ||
                     node == this .dropDown.previousButton){
                     newValue = node.innerHTML;
                     this .item = undefined;
                     this .value = '' ;
                 } else {
                     var item = this .dropDown.items[node.getAttribute( "item" )];
                     var displayAttr = this .displayValueAttr!= null ? this .displayValueAttr: this .searchAttr; //此处判断是否配置了自定义属性displayValueAttr
                     
                     newValue = ( this .store._oldAPI ?    // remove getValue() for 2.0 (old dojo.data API)
                         this .store.getValue(item, displayAttr) : item[displayAttr]).toString(); //将this.searchAttr替换为displayAttr
 
                     this .set( 'item' , item, false , newValue);
                 }
                 // get the text that the user manually entered (cut off autocompleted text)
                 this .focusNode.value = this .focusNode.value.substring(0, this ._lastInput.length);
                 // set up ARIA activedescendant
                 this .focusNode.setAttribute( "aria-activedescendant" , domAttr.get(node, "id" ));
                 // autocomplete the rest of the option to announce change
                 this ._autoCompleteText(newValue);
             },
 
     });
});

将页面中引入FilteringSelect换成我们自定义的FilteringSelect,然后在创建FilteringSelect时的代码中加入自定义的displayValueAttr属性。

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<script type= "text/javascript" >
     require([
         'test/FilteringSelect' ,
         'dojo/store/Memory' ,
         'dojo/domReady!'
     ], function (FilteringSelect,Memory){
             
           //下拉菜单的store,"py"字段存放每个条目的拼音简码
           var selectStore = new Memory({
                 data:[
                     {name: '赵云' ,id: '1' ,py: 'zy' },    
                     {name: '张飞' ,id: '2' ,py: 'zf' },    
                     {name: '刘备' ,id: '3' ,py: 'lb' },    
                     {name: '关羽' ,id: '4' ,py: 'gy' },    
                     {name: '黄忠' ,id: '5' ,py: 'hz' },    
                     {name: '魏延' ,id: '6' ,py: 'wy' },    
                     {name: '周瑜' ,id: '7' ,py: 'zy' },    
                     {name: '孙坚' ,id: '8' ,py: 'sj' },    
                     {name: '曹操' ,id: '9' ,py: 'cc' },    
                     {name: '夏侯敦' ,id: '10' ,py: 'xhd' },    
                 ]
           });
           
           //创建FilteringSelect
           var testSelect = new FilteringSelect({
               id: "testSelect" ,
               name: "test" ,
               value: "" ,
               store: selectStore,
               searchAttr: 'py' , //指定输入文本框进行用来进行检索的字段
               labelAttr: 'name' , //指定下拉菜单中显示的字段
               displayValueAttr: 'name' , //指定选中下拉菜单后显示在输入框中的字段
               required: false ,
               autoComplete: false
           }, "testSelect" );
         
     });
</script>

再次访问本页面,可以看到在选择下拉菜单条目后,text输入框显示为name的中文了。
使用Dojo的FilteringSelect打造具有拼音检索功能的下拉菜单(上)

下篇预告:
准备写一下服务端自动实现新增实体bean时,将相应name字段转为拼音简码后存储。大致过程是编写一个java注解,在model实体类中标注需要进行汉字转拼音的属性字段,利用spring的AOP功能,编写一个统一切面,在dao层对保存实体的save方法进行拦截,将标注的name字段转为拼音简码后注入bean的拼音简码字段,这样使业务编码人员无需再关注对实体name字段进行拼音简码的转换工作。