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

前言

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

分析

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

问题

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

2.用什么方式来筛选?是每次显示匹配项,隐藏不匹配项还是毎次从数据源里匹配,重新生成结点。

解决思路

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来存数据。

总结

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

(0)

相关推荐

  • AngularJS模糊查询功能实现代码(过滤内容下拉菜单排序过滤敏感字符验证判断后添加表格信息)

    注:添加球员的功能无指定技术要求,添加球员的页面也无具体样式要求. 1.实现上图页面所有元素,页面布局规整,跟上图效果一致 2.实现文案显示,按效果显示 3.实现查询,实现查询敏感词过滤,实现查询后列表变化 4.实现倒序,实现正序,下拉列表排序效果都实现 5.按钮背景一致,按钮样式 6.实现添加球员页面,添加球员页面样式,添加球员功能,添加球员必填项判断,添加完球员后能显示在表格内,已存在球员判重. 7.表格样式跟上图样式一致 代码: <!DOCTYPE html> <html lang

  • angularjs实现下拉列表的选中事件示例

    select标签的对于大家来说很熟悉了,下面我来讲讲AngularJS中 对select的属性设置,做出选择某个下拉选项后控制其他标签的隐藏: <select style="height:31px; width:100px" id="rewardMethod"name="rewardMethod" class="status_select" ng-model="state"> <optio

  • AngularJS service之select下拉菜单效果

    本文实例为大家分享了service之select下拉菜单效果的具体代码,供大家参考,具体内容如下 <!-- $watch:持续监听数据上的变化,更新界面 --> <!DOCTYPE html> <html lang="en" ng-app="myApp" ng-controller="myCtrl"> <head> <meta charset="utf-8"> <

  • Angularjs实现下拉框联动的示例代码

    第一种联动方式,在网上看到的,感觉对于我的使用性不高,比较后端不会提供这种json... 实现截图 html <select ng-model="s1" ng-options="selectData.name for selectData in selectDatas"> <option value="">--产品类目--</option> </select> <select ng-model

  • 基于AngularJs + Bootstrap + AngularStrap相结合实现省市区联动代码

    Angular JS (Angular.JS) 是一组用来开发Web页面的框架.模板以及数据绑定和丰富UI组件.它支持整个开发进程,提供web应用的架构,无需进行手工DOM操作. AngularJs 就是一个函数库,算不上一个框架,源码2万2千多行,提供了前端MVC的开发方式,有双向绑定,指令等特性,这是具有革命性的.我是多么反感jQuery 用选择器 选择元素 ,绑定事件,进行一大堆DOM操作,一旦代码过多,非常不好维护,html结构改变,又要重写js代码,不过 jQuery  对 ajax的

  • angularjs下拉框空白的解决办法

    搜索angularjs下拉框空白,可以出现很多解决方案,但是对于静态字段来说,网上目前还没有找到解决方案,如下: <select class="form-control" ng-model="UserState" ng-init="UserState=0"> <option value="-1">选择状态</option> <option value="0">

  • AngularJS中下拉框的高级用法示例

    本文实例讲述了AngularJS中下拉框的高级用法.分享给大家供大家参考,具体如下: HTML正文: <body ng-app="myApp"> <!-- 对象内部属性遍历:x--key y---value --> <div ng-controller="myctr01"> {{sites}}<br> <select ng-model="site" ng-options="x for

  • AngularJS使用ngOption实现下拉列表的实例代码

    下拉列表的简单使用 ng-option指令使用很简单,只需要绑定两个属性: 一个是ng-model用于获取选定的值: 另一个是ng-options用于确定下拉列表的元素数组. <select ng-model="engineer.currentActivity" class="form-control" ng-options="act for act in activities"></select> 上面这条语句就是把选择

  • Angularjs自定义指令实现三级联动 选择地理位置

    Angularjs自定义指令实现三级联动效果,先上图 代码 <html lang="zh-CN" ng-app="myApp"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport&quo

  • 实例详解AngularJS实现无限级联动菜单

    多级联动菜单是常见的前端组件,比如省份-城市联动.高校-学院-专业联动等等.场景虽然常见,但仔细分析起来要实现一个通用的无限分级联动菜单却不一定像想象的那么简单.比如,我们需要考虑子菜单的加载是同步的还是异步的?对于初始值的回填发生在前端还是后端?如果异步加载,是否对于后端API的返回格式有严格的定义?是否容易实现同步.异步共存?是否可以灵活的支持各类依赖关系?菜单中是否有空值选项?--一系列的问题都需要精心处理. 带着这些需求搜索了一圈,不太出乎意料,并没有能在AngularJS的生态中找到一

  • AngularJS实现的select二级联动下拉菜单功能示例

    本文实例讲述了AngularJS实现的select二级联动下拉菜单功能.分享给大家供大家参考,具体如下: <!DOCTYPE html> <html ng-app> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <script src="../script/angular.js"

随机推荐