Angularjs实现带查找筛选功能的select下拉框示例代码
前言
对于select的下拉列表,像国家选择这样的功能,全世界那么多国家,一直拉滚动条多辛苦,眼睛也要盯着找,累!所以为优化用户体验,带查找功能的下拉框是非常非常有必要的。都知道jquery里有这样的插件,但我们用的是Angularjs,更希望用双向绑定,指令的方式优雅地解决这个问题。
分析
我们的目标是在原来的<selectng-options="">标签上新加一个属性select-search就能支持查找的功能。如果这个属性没起作用,也不影响原来的select的功能。
问题
1.在selectSearch指令里,怎么获取到ng-options里的数据源,以及指定的value(option标签的value)和text(option标签里的text)字段名。
2.用什么方式来筛选?是每次显示匹配项,隐藏不匹配项还是毎次从数据源里匹配,重新生成结点。
解决思路
1.参考angular自带指令ng-options来获取数据源和value,text字段名。特别说明,仅支持ng-options="obj.valueasobj.textforobjinlist"的普通形式,那些带分组的等等,暂不支持哈。
2.重新生成结点。(为什么这么选择,方便呀!)
具体实现
1.代码部分
1.1js代码(请引先引入jquery,不然会报错)
/** *带筛选功能的下拉框 *使用方法<selectngc-select-searchname="select1"ng-options=""> *说明[select一定要有name,ng-options属性] */ .directive('ngcSelectSearch',function($animate,$compile,$parse){ functionparseOptions(optionsExp,element,scope){ //ngOptions里的正则 varNG_OPTIONS_REGEXP=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/; varmatch=optionsExp.match(NG_OPTIONS_REGEXP); if(!(match)){ console.log('ng-options表达式有误') } varvalueName=match[5]||match[7]; varkeyName=match[6]; vardisplayFn=$parse(match[2]); varkeyFn=$parse(match[1]); varvaluesFn=$parse(match[8]); varlabelArray=[], idArray=[], optionValues=[]; scope.$watch(match[8],function(newValue,oldValue){ if(newValue&&newValue.length>0){ optionValues=valuesFn(scope)||[]; labelArray=[]; idArray=[] for(varindex=0,l=optionValues.length;index<l;index++){ varit=optionValues[index]; if(match[2]&&match[1]){ varlocalIt={}; localIt[valueName]=it; varlabel=displayFn(scope,localIt); vardataId=keyFn(scope,localIt); labelArray.push(label); idArray.push(dataId); } } scope.options={ 'optionValues':optionValues, 'labelArray':labelArray, 'idArray':idArray } } }); } return{ restrict:'A', require:['ngModel'], priority:100, replace:false, scope:true, template:'<divclass="chose-container">'+ '<divclass="chose-single"><spanclass="j-view"></span><iclass="glyphiconglyphicon-remove"></i></div>'+ '<divclass="chose-dropchose-hidej-drop">'+ '<divclass="chose-search">'+ '<inputclass="j-key"type="text"autocomplete="off">'+ '</div>'+ '<ulclass="chose-result">'+ //'<ling-repeat="'+repeatTempl+'"data-id="'+keyTempl+'">{{'+valueTempl+'}}</li>'+ '</ul>'+ '</div>'+ '</div>', link:{ pre:functionselectSearchPreLink(scope,element,attr,ctrls){ vartmplNode=$(this.template).first(); varmodelName=attr.ngModel, name=attr.name?attr.name:('def'+Date.now()); tmplNode.attr('id',name+'_chosecontianer'); $animate.enter(tmplNode,element.parent(),element); }, post:functionselectSearchPostLink(scope,element,attr,ctrls){ varchoseNode=element.next();//$('#'+attr.name+'_chosecontianer'); choseNode.addClass(attr.class); element.addClass('chose-hide'); //当前选中项 varngModelCtrl=ctrls[0]; if(!ngModelCtrl||!attr.name)return; parseOptions(attr.ngOptions,element,scope); varrs={}; functionsetView(){ varcurrentKey=ngModelCtrl.$modelValue; if(isNaN(currentKey)||!currentKey){ currentKey=''; choseNode.find('.j-view:first').text('请选择'); choseNode.find('i').addClass('chose-hide'); } if((currentKey+'').length>0){ for(vari=0,l=rs.idArray.length;i<l;i++){ if(rs.idArray[i]==currentKey){ choseNode.find('.j-view:first').text(rs.labelArray[i]); choseNode.find('i').removeClass('chose-hide'); break; } } } } functionsetViewAndData(){ if(!scope.options){ return; } rs=scope.options; setView(); } scope.$watchCollection('options',setViewAndData); scope.$watch(attr.ngModel,setView); functiongetListNodes(value){ varnodes=[]; value=$.trim(value); for(vari=0,l=rs.labelArray.length;i<l;i++){ if(rs.labelArray[i].indexOf(value)>-1){ nodes.push($('<li>').data('id',rs.idArray[i]).text(rs.labelArray[i])) } } returnnodes; } choseNode.on('keyup','.j-key',function(){ //搜索输入框keyup,重新筛选列表 varvalue=$(this).val(); choseNode.find('ul:first').empty().append(getListNodes(value)); returnfalse; }).on('click',function(){ choseNode.find('.j-drop').removeClass('chose-hide'); if(choseNode.find('.j-view:first').text()!='请选择'){ choseNode.find('i').removeClass('chose-hide'); } choseNode.find('ul:first').empty().append(getListNodes(choseNode.find('.j-key').val())); returnfalse; }).on('click','ul>li',function(){ var_this=$(this); ngModelCtrl.$setViewValue(_this.data('id')); ngModelCtrl.$render(); choseNode.find('.j-drop').addClass('chose-hide'); returnfalse; }).on('click','i',function(){ ngModelCtrl.$setViewValue(''); ngModelCtrl.$render(); choseNode.find('.j-view:first').text('请选择'); returnfalse; }); $(document).on("click",function(){ $('.j-drop').addClass('chose-hide'); choseNode.find('i').addClass('chose-hide'); returnfalse; }); } } }; })
1.2css代码(用less写的,以下是编译后的)
.chose-hide{ position:absolute!important; top:-999em!important; } .chose-container{ border:none!important; float:left; margin-right:40px; padding:0!important; position:relative; } .chose-container.chose-single{ padding:6px12px; color:#333; width:100%; border:1pxsolid#eee; display:inline-block; height:30px; } .chose-container.chose-single::after{ content:''; position:absolute; border-width:6px3px; border-style:solid; /*border-top-color:transparent;*/ border-left-color:transparent; border-right-color:transparent; border-bottom-color:transparent; right:8px; top:12px; } .chose-container.chose-singlei{ width:12px; float:right; right:8px; font-size:12px; height:12px; background-color:#eee; } .chose-container.chose-drop{ width:195px; position:absolute; border:1pxsolid#eee; z-index:1000; background-color:#fff; } .chose-container.chose-searchinput[type='text']{ margin:0; padding-left:12px; width:100%; height:30px; border:1pxsolid#ccc; float:none; } .chose-container.chose-result{ max-height:370px; overflow-y:scroll; overflow-x:hidden; } .chose-container.chose-resultli{ padding:5px12px; list-style-type:none; } .chose-container.chose-resultli:hover{ background-color:#e1e2e7; }
使用及效果
<selectngc-select-searchclass="common-select"ng-model="aa.b"ng-options="obj.countryIdasobj.countryCnNameforobjinvm.countries"name="country"> <optionvalue="">请选择</option></select>
详细说明
程序中的关键点是parseOptions函数,即前面分析里的问题1。parseOptions是参考ng-options的源码实现的,原来是想返回一个对象,这个对象里包含了数据源,但是在调试时,发现post函数中该函数返回对象里的数据为空,watch不到,所以改为用scope.options来存数据。
总结
以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。