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 相关文章推荐
js/html光标定位的实现代码
Sep 23 Javascript
js中文逗号转英文实现
Feb 11 Javascript
使用Javascript简单实现图片无缝滚动
Dec 05 Javascript
JavaScript实现的链表数据结构实例
Apr 02 Javascript
不得不看之JavaScript构造函数及new运算符
Aug 21 Javascript
jQuery选择器之子元素过滤选择器
Sep 28 jQuery
vue用递归组件写树形控件的实例代码
Jul 19 Javascript
使用electron实现百度网盘悬浮窗口功能的示例代码
Oct 24 Javascript
解决VUE双向绑定失效的问题
Oct 29 Javascript
vue中实现回车键登录功能
Feb 19 Javascript
Vue——解决报错 Computed property &quot;****&quot; was assigned to but it has no setter.
Dec 19 Vue.js
Vue实现tab导航栏并支持左右滑动功能
Jun 28 Vue.js
用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
双料怀旧--SHARP GF515的维护、修理和简单调试
2021/03/02 无线电
基于mysql的论坛(1)
2006/10/09 PHP
获取php页面执行时间,数据库读写次数,函数调用次数等(THINKphp)
2013/06/03 PHP
php批量上传的实现代码
2013/06/09 PHP
php抽奖小程序的实现代码
2013/06/18 PHP
destoon官方标签大全
2014/06/20 PHP
PHP实现在线阅读PDF文件的方法
2015/06/23 PHP
php实现分页功能的详细实例方法
2019/09/29 PHP
jQuery 打造动态下滑菜单实现说明
2010/04/15 Javascript
基于jquery的cookie的用法
2011/01/10 Javascript
jquery load()在firefox(火狐)下显示不正常的解决方法
2011/04/05 Javascript
jQuery实现密保互斥问题解决方案
2013/08/16 Javascript
node.js中的buffer.Buffer.isBuffer方法使用说明
2014/12/14 Javascript
JavaScript实现为指定对象添加多个事件处理程序的方法
2015/04/17 Javascript
js中的内部属性与delete操作符介绍
2015/08/10 Javascript
thinkphp标签实现bootsrtap轮播carousel实例代码
2017/02/19 Javascript
JS+Ajax实现百度智能搜索框
2017/08/04 Javascript
微信小程序实现动态设置页面标题的方法【附源码下载】
2017/11/29 Javascript
Angular 向组件传递模板的两种方法
2018/02/23 Javascript
vue移动端屏幕适配详解
2019/04/30 Javascript
python通过邮件服务器端口发送邮件的方法
2015/04/30 Python
Python中的super()方法使用简介
2015/08/14 Python
python装饰器-限制函数调用次数的方法(10s调用一次)
2018/04/21 Python
详解Python图像处理库Pillow常用使用方法
2019/09/02 Python
关于tensorflow softmax函数用法解析
2020/06/30 Python
Python通过getattr函数获取对象的属性值
2020/10/16 Python
荷兰网上买鞋:MooieSchoenen.nl
2017/09/12 全球购物
C++面试题:关于链表和指针
2013/06/05 面试题
个人对照检查材料思想汇报(四风问题)
2014/09/25 职场文书
2015年试用期工作总结范文
2015/05/28 职场文书
网络妈妈观后感
2015/06/08 职场文书
2015年校本培训工作总结
2015/07/24 职场文书
出纳2015年度工作总结范文
2015/10/14 职场文书
常用的Python代码调试工具总结
2021/06/23 Python
html+css实现滚动到元素位置显示加载动画效果
2021/08/02 HTML / CSS
Nginx的基本概念和原理
2022/03/21 Servers