Angularjs实现带查找筛选功能的select下拉框示例代码


Posted in Javascript onOctober 04, 2016

前言

对于select的下拉列表,像国家选择这样的功能,全世界那么多国家,一直拉滚动条多辛苦,眼睛也要盯着找,累!所以为优化用户体验,带查找功能的下拉框是非常非常有必要的。都知道jquery里有这样的插件,但我们用的是Angularjs,更希望用双向绑定,指令的方式优雅地解决这个问题。

分析

我们的目标是在原来的<select ng-options="">标签上新加一个属性 select-search 就能支持查找的功能。如果这个属性没起作用,也不影响原来的select的功能。

问题

     1.在selectSearch指令里,怎么获取到ng-options里的数据源,以及指定的value(option标签的value)text(option标签里的text)字段名。

     2.用什么方式来筛选?是每次显示匹配项,隐藏不匹配项还是?按未邮?菰蠢锲ヅ洌?匦律?山岬恪?/p>

解决思路

     1.参考angular自带指令ng-options来获取数据源和value,text字段名。特别说明,仅支持ng-options="obj.value as obj.text for obj in list"的普通形式,那些带分组的等等,暂不支持哈。

     2.重新生成结点。(为什么这么选择,方便呀!)

具体实现

1.代码部分

1.1 js代码(请引先引入jquery,不然会报错)

/**
  * 带筛选功能的下拉框
  * 使用方法 <select ngc-select-search name="select1" ng-options="">
  * 说明[ select 一定要有name,ng-options 属性]
  */
 .directive('ngcSelectSearch', function($animate, $compile, $parse) {

   function parseOptions(optionsExp, element, scope) {
     // ngOptions里的正则
     var NG_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]+?))?$/;

     var match = optionsExp.match(NG_OPTIONS_REGEXP);
     if (!(match)) {
       console.log('ng-options 表达式有误')
     }
     var valueName = match[5] || match[7];
     var keyName = match[6];
     var displayFn = $parse(match[2]);
     var keyFn = $parse(match[1]);
     var valuesFn = $parse(match[8]);

     var labelArray = [],
       idArray = [],
       optionValues = [];
     scope.$watch(match[8], function(newValue, oldValue) {
       if (newValue && newValue.length > 0) {
         optionValues = valuesFn(scope) || [];
         labelArray = [];
         idArray = []
         for (var index = 0, l = optionValues.length; index < l; index++) {
           var it = optionValues[index];
           if (match[2] && match[1]) {
             var localIt = {};
             localIt[valueName] = it;
             var label = displayFn(scope, localIt);
             var dataId = 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: '<div class="chose-container">' +
       '<div class="chose-single"><span class="j-view"></span><i class="glyphicon glyphicon-remove"></i></div>' +
       '<div class="chose-drop chose-hide j-drop">' +
       '<div class="chose-search">' +
       '<input class="j-key" type="text" autocomplete="off">' +
       '</div>' +
       '<ul class="chose-result">' +
       // '<li ng-repeat="'+repeatTempl+'" data-id="'+keyTempl+'" >{{'+ valueTempl+'}}</li>'+
       '</ul>' +
       '</div>' +
       '</div>',
     link: {
       pre: function selectSearchPreLink(scope, element, attr, ctrls) {

         var tmplNode = $(this.template).first();

         var modelName = attr.ngModel,
           name = attr.name? attr.name:('def'+Date.now());
         tmplNode.attr('id', name + '_chosecontianer');

         $animate.enter(tmplNode, element.parent(), element);
       },
       post: function selectSearchPostLink(scope, element, attr, ctrls) {
         var choseNode = element.next(); //$('#'+attr.name +'_chosecontianer');
         choseNode.addClass(attr.class);
         element.addClass('chose-hide');
         // 当前选中项
         var ngModelCtrl = ctrls[0];
         if (!ngModelCtrl || !attr.name) return;

         parseOptions(attr.ngOptions, element, scope);
         var rs = {};

         function setView() {
           var currentKey = ngModelCtrl.$modelValue;
           if (isNaN(currentKey) || !currentKey) {
             currentKey = '';
             choseNode.find('.j-view:first').text('请选择');
             choseNode.find('i').addClass('chose-hide');
           }
           if ((currentKey + '').length > 0) {
             for (var i = 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;
               }
             }
           }
         }

         function setViewAndData() {
           if (!scope.options) {
             return;
           }
           rs = scope.options;
           setView();
         }
         scope.$watchCollection('options', setViewAndData);
         scope.$watch(attr.ngModel, setView);


         function getListNodes(value) {
           var nodes = [];
           value = $.trim(value);
           for (var i = 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]))
             }
           }
           return nodes;

         }
         choseNode.on('keyup', '.j-key', function() {
           // 搜索输入框keyup,重新筛选列表
           var value = $(this).val();
           choseNode.find('ul:first').empty().append(getListNodes(value));
           return false;
         }).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()));
           return false;
         }).on('click', 'ul>li', function() {
           var _this = $(this);
           ngModelCtrl.$setViewValue(_this.data('id'));
           ngModelCtrl.$render();
           choseNode.find('.j-drop').addClass('chose-hide');
           return false;

         }).on('click', 'i', function() {
           ngModelCtrl.$setViewValue('');
           ngModelCtrl.$render();
           choseNode.find('.j-view:first').text('请选择');
           return false;

         });
         $(document).on("click", function() {
           $('.j-drop').addClass('chose-hide');
           choseNode.find('i').addClass('chose-hide');
           return false;
         });

       }
     }
   };
 })

1.2 css代码(用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: 6px 12px;
 color: #333;
 width: 100%;
 border: 1px solid #eee;
 display: inline-block;
 height: 30px;
}
.chose-container .chose-single::after {
 content: '';
 position: absolute;
 border-width: 6px 3px;
 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-single i {
 width: 12px;
 float: right;
 right: 8px;
 font-size: 12px;
 height: 12px;
 background-color: #eee;
}
.chose-container .chose-drop {
 width: 195px;
 position: absolute;
 border: 1px solid #eee;
 z-index: 1000;
 background-color: #fff;
}
.chose-container .chose-search input[type='text'] {
 margin: 0;
 padding-left: 12px;
 width: 100%;
 height: 30px;
 border: 1px solid #ccc;
 float: none;
}
.chose-container .chose-result {
 max-height: 370px;
 overflow-y: scroll;
 overflow-x: hidden;
}
.chose-container .chose-result li {
 padding: 5px 12px;
 list-style-type: none;
}
.chose-container .chose-result li:hover {
 background-color: #e1e2e7;
}

使用及效果

<select ngc-select-search class="common-select" ng-model="aa.b" ng-options="obj.countryId as obj.countryCnName for obj in vm.countries" name="country">
<option value="">请选择</option></select>

详细说明

程序中的关键点是parseOptions函数,即前面分析里的问题1。parseOptions是参考ng-options的源码实现的,原来是想返回一个对象,这个对象里包含了数据源,但是在调试时,发现post函数中该函数返回对象里的数据为空,watch不到,所以改为用scope.options来存数据。

总结

以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
window.onload 加载完毕的问题及解决方案(上)
Jul 09 Javascript
JavaScript中getUTCMinutes()方法的使用详解
Jun 10 Javascript
js实现带有介绍的Select列表菜单实例
Aug 18 Javascript
js限制文本框的输入内容代码分享(3类)
Aug 20 Javascript
jQuery ajax请求返回list数据动态生成input标签,并把list数据赋值到input标签
Mar 29 Javascript
jQuery实现带延时功能的水平多级菜单效果【附demo源码下载】
Sep 21 Javascript
JS计算输出100元钱买100只鸡问题的解决方法
Jan 04 Javascript
微信小程序ibeacon三点定位详解
Oct 31 Javascript
angular6开发steps步骤条组件
Jul 04 Javascript
layui 实现自动选择radio单选框(checked)的方法
Sep 03 Javascript
JS数组进阶示例【数组的几种函数用法】
Jan 16 Javascript
TS 类型兼容教程示例详解
Sep 23 Javascript
用jquery快速解决IE输入框不能输入的问题
Oct 04 #Javascript
JavaScript九九乘法口诀表的简单实现
Oct 04 #Javascript
原生js仿jquery实现对Ajax的封装
Oct 04 #Javascript
json定义及jquery操作json的方法
Oct 03 #Javascript
javascript中异常处理案例(推荐)
Oct 03 #Javascript
switch语句的妙用(必看篇)
Oct 03 #Javascript
js中scrollTop()方法和scroll()方法用法示例
Oct 03 #Javascript
You might like
PHP查看SSL证书信息的方法
2016/09/22 PHP
php使用pthreads v3多线程实现抓取新浪新闻信息操作示例
2020/02/21 PHP
JQuery select控件的相关操作实现代码
2012/09/14 Javascript
使用documentElement正确取得当前可见区域的大小
2014/07/25 Javascript
jQuery事件用法实例汇总
2014/08/29 Javascript
鼠标事件的screenY,pageY,clientY,layerY,offsetY属性详解
2015/03/12 Javascript
jquery拖拽效果完整实例(附demo源码下载)
2016/01/14 Javascript
JavaScript中关于for循环删除数组元素内容时出现的问题
2016/11/21 Javascript
jQuery插件HighCharts绘制简单2D折线图效果示例【附demo源码】
2017/03/21 jQuery
关于jQuery库冲突的完美解决办法
2017/05/20 jQuery
什么是Vue.js框架 为什么选择它?
2017/10/17 Javascript
vue语法之拼接字符串的示例代码
2017/10/25 Javascript
微信小程序checkbox组件使用详解
2018/01/31 Javascript
vue中改变选中当前项的显示隐藏或者状态的实现方法
2018/02/08 Javascript
vue2.0 elementUI制作面包屑导航栏
2018/02/22 Javascript
Node.js Windows Binary二进制文件安装方法
2019/05/16 Javascript
微信小程序实现手指拖动选项排序
2020/04/22 Javascript
Python获取当前路径实现代码
2017/05/08 Python
Python基于回溯法子集树模板解决全排列问题示例
2017/09/07 Python
django manage.py扩展自定义命令方法
2018/05/27 Python
MNIST数据集转化为二维图片的实现示例
2020/01/10 Python
如何基于pythonnet调用halcon脚本
2020/01/20 Python
python 实现在shell窗口中编写print不向屏幕输出
2020/02/19 Python
Django模板标签{% for %}循环,获取制定条数据实例
2020/05/14 Python
Python把图片转化为pdf代码实例
2020/07/28 Python
南非领先的在线旅行社:Travelstart南非
2016/09/04 全球购物
英国二手iPhone、音乐、电影和游戏商店:musicMagpie
2018/10/26 全球购物
New Balance澳大利亚官网:运动鞋和健身服装
2019/02/23 全球购物
旅游管理本科生求职信
2013/10/14 职场文书
同学聚会老师邀请函
2014/01/28 职场文书
药店主任岗位责任制
2014/02/10 职场文书
幼儿园评语大全
2014/04/17 职场文书
税务会计岗位职责
2015/04/02 职场文书
卫生保健工作总结2015
2015/05/18 职场文书
少先队入队仪式主持词
2015/07/04 职场文书
利用python Pandas实现批量拆分Excel与合并Excel
2021/05/23 Python