angularjs 实现带查找筛选功能的select下拉框实例


Posted in Javascript onJanuary 11, 2017

一.背景

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

分析:

   目标 在原来的
   问题 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;
}

1.3 使用及效果

<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>

angularjs 实现带查找筛选功能的select下拉框实例

2.详细说明

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
用javascript操作xml
Nov 04 Javascript
JS操作Cookies的小例子
Oct 15 Javascript
js去除输入框中所有的空格和禁止输入空格的方法
Jun 09 Javascript
Javascript 绘制 sin 曲线过程附图
Aug 21 Javascript
html的DOM中document对象images集合用法实例
Jan 21 Javascript
JavaScript前补零操作实例
Mar 11 Javascript
JavaScript之Object类型介绍
Apr 01 Javascript
Vue.js每天必学之表单控件绑定
Sep 05 Javascript
AjaxUpLoad.js实现文件上传功能
Mar 02 Javascript
react实现antd线上主题动态切换功能
Aug 12 Javascript
详解ES6数组方法find()、findIndex()的总结
May 12 Javascript
JS如何在不同平台实现多语言方式
Jul 16 Javascript
微信小程序 轮播图swiper详解及实例(源码下载)
Jan 11 #Javascript
Angularjs中使用layDate日期控件示例
Jan 11 #Javascript
web打印小结
Jan 11 #Javascript
微信小程序 常见问题总结(4058,40013)及解决办法
Jan 11 #Javascript
jQuery插件JWPlayer视频播放器用法实例分析
Jan 11 #Javascript
AngularJS中的缓存使用
Jan 11 #Javascript
AngularJS中的按需加载ocLazyLoad示例
Jan 11 #Javascript
You might like
php操作MongoDB基础教程(连接、新增、修改、删除、查询)
2014/03/25 PHP
ThinkPHP实现事务回滚示例代码
2014/06/23 PHP
thinkphp普通查询与表达式查询实例分析
2014/11/24 PHP
thinkPHP简单实现多个子查询语句的方法
2016/12/05 PHP
浅谈laravel aliases别名的原理
2019/10/24 PHP
PHP实现递归的三种方法
2020/07/04 PHP
javascript 贪吃蛇实现代码
2008/11/22 Javascript
热点新闻滚动特效的js代码
2013/08/17 Javascript
为JS扩展Array.prototype.indexOf引发的问题及解决办法
2015/01/21 Javascript
JavaScript使用Math.Min返回两个数中较小数的方法
2015/04/06 Javascript
利用imgareaselect辅助后台实现图片上传裁剪
2017/03/02 Javascript
Express URL跳转(重定向)的实现方法
2017/04/07 Javascript
微信小程序中hidden不生效原因的解决办法
2017/04/26 Javascript
JS非行间样式获取函数的实例代码
2018/06/05 Javascript
谈谈JavaScript中super(props)的重要性
2019/02/12 Javascript
VUE脚手架具体使用方法
2019/05/20 Javascript
JavaScript 预解析的4种实现方法解析
2019/09/03 Javascript
Python中的浮点数原理与运算分析
2017/10/12 Python
python实现kMeans算法
2017/12/21 Python
python docx 中文字体设置的操作方法
2018/05/08 Python
Django框架使用内置方法实现登录功能详解
2019/06/12 Python
python__name__原理及用法详解
2019/11/02 Python
python如何绘制疫情图
2020/09/16 Python
Python实现区域填充的示例代码
2021/02/03 Python
canvas如何实现多张图片编辑的图片编辑器
2020/03/10 HTML / CSS
北卡罗来纳州豪华家具和家居装饰店:Carolina Rustica
2018/10/30 全球购物
得到Class的三个过程是什么
2012/08/10 面试题
JSF面试题:如何管量web层中的Bean,用什么标签。如何通过jsp页面与Bean绑定在一起进行处理?
2012/10/05 面试题
精选干货:Java精选笔试题附答案
2014/01/18 面试题
学生会宣传部部长竞选演讲稿
2014/04/25 职场文书
中文专业自荐书
2014/06/29 职场文书
2014物价局群众路线对照检查材料思想汇报
2014/09/21 职场文书
毕业生见习报告总结
2014/11/08 职场文书
2014年村计划生育工作总结
2014/11/14 职场文书
2015年办公室主任工作总结
2015/04/09 职场文书
浅谈Python项目的服务器部署
2021/04/25 Python