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来存数据。
总结
以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。