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 相关文章推荐
获取URL地址中的文件名和参数的javascript代码
Sep 02 Javascript
基于jquery的禁用右键、文本选择功能、复制按键的实现代码
Aug 27 Javascript
Node.js中安全调用系统命令的方法(避免注入安全漏洞)
Dec 05 Javascript
jQuery自动添加表单项的方法
Jul 13 Javascript
Bootstrap学习笔记之css组件(3)
Jun 07 Javascript
对Js OOP编程 创建对象的一些全面理解
Jul 26 Javascript
js实现密码强度检验
Jan 15 Javascript
JS中绑定事件顺序(事件冒泡与事件捕获区别)
Jan 24 Javascript
微信小程序中显示html格式内容的方法
Apr 25 Javascript
微信小程序实现带缩略图轮播效果
Nov 04 Javascript
如何实现一个简易版的vuex持久化工具
Sep 11 Javascript
Map与WeakMap类型在JavaScript中的使用详解
Nov 18 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随机数生成代码与使用实例分析
2011/04/08 PHP
PHP中的函数嵌套层数限制分析
2011/06/13 PHP
深入PHP数据缓存的使用说明
2013/05/10 PHP
Laravel实现表单提交
2017/05/07 PHP
php之可变变量的实例详解
2017/09/12 PHP
javascript 复杂的嵌套环境中输出单引号和双引号
2009/05/26 Javascript
js word表格动态添加代码
2010/06/07 Javascript
jquery实现按Enter键触发事件示例
2013/09/10 Javascript
Javascript中arguments用法实例分析
2015/06/13 Javascript
基于jquery步骤进度条源码分享
2015/11/12 Javascript
分享两款带遮罩的jQuery弹出框
2015/12/30 Javascript
JavaScript获取URL参数的方法之一
2017/03/24 Javascript
angular中ui calendar的一些使用心得(推荐)
2017/11/03 Javascript
微信小程序自定义底部弹出框
2020/11/16 Javascript
使用electron制作满屏心特效的示例代码
2018/11/27 Javascript
Angular脚手架开发的实现步骤
2019/04/09 Javascript
js中火星坐标、百度坐标、WGS84坐标转换实现方法示例
2020/03/02 Javascript
[03:56]DOTA2完美大师赛趣味视频之小鸽子和Mineski打台球
2017/11/24 DOTA
详解python的ORM中Pony用法
2018/02/09 Python
Django uwsgi Nginx 的生产环境部署详解
2019/02/02 Python
python3中使用__slots__限定实例属性操作分析
2020/02/14 Python
使用sklearn对多分类的每个类别进行指标评价操作
2020/06/11 Python
matplotlib绘制多子图共享鼠标光标的方法示例
2021/01/08 Python
HTML5实现应用程序缓存(Application Cache)
2020/06/16 HTML / CSS
建筑工程自我鉴定
2013/10/18 职场文书
大学学年自我鉴定
2013/10/28 职场文书
英语文学专业学生的自我评价
2013/10/31 职场文书
优秀学生干部个人事迹材料
2014/06/02 职场文书
法律专业自荐信
2014/06/03 职场文书
群众路线党员个人整改措施
2014/10/27 职场文书
班主任自我评价范文
2015/03/11 职场文书
学校2015年纠风工作总结
2015/05/15 职场文书
2016大学生党校学习心得体会
2016/01/06 职场文书
基于JavaScript实现省市联动效果
2021/06/22 Javascript
Vue提供的三种调试方式你知道吗
2022/01/18 Vue.js
JavaScript ES6的函数拓展
2022/01/18 Javascript