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 相关文章推荐
jQuery Flash/MP3/Video多媒体插件
Jan 18 Javascript
JavaScript.The.Good.Parts阅读笔记(一)假值与===运算符
Nov 16 Javascript
基于jquery的使ListNav兼容中文首字拼音排序的实现代码
Jul 10 Javascript
javascript如何创建表格(javascript绘制表格的二种方法)
Dec 10 Javascript
在Linux上用forever实现Node.js项目自启动
Jul 09 Javascript
javascript中的遍历for in 以及with的用法
Dec 22 Javascript
JavaScript中的replace()方法使用详解
Jun 06 Javascript
使用基于Node.js的构建工具Grunt来发布ASP.NET MVC项目
Feb 15 Javascript
JavaScript实现多种排序算法
Feb 24 Javascript
原生js实现可爱糖果数字时间特效
Dec 30 Javascript
axios学习教程全攻略
Mar 26 Javascript
基于vue+element实现全局loading过程详解
Jul 10 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
一个简单的自动发送邮件系统(三)
2006/10/09 PHP
微信支付开发教程(一)微信支付URL配置
2014/05/28 PHP
PHP在线调试执行的实现方法(附demo源码)
2016/04/28 PHP
Laravel框架实现利用监听器进行sql语句记录功能
2018/06/06 PHP
jQuery中serializeArray()与serialize()的区别实例分析
2015/12/09 Javascript
Bootstrap表格和栅格分页实例详解
2016/05/20 Javascript
利用js查找数组中指定元素并返回该元素的所有索引示例
2017/03/29 Javascript
JavaScript实现form表单的多文件上传
2020/03/27 Javascript
jquery 一键复制到剪切板的实例
2017/09/20 jQuery
详解vue中async-await的使用误区
2018/12/05 Javascript
小程序实现按下录音松开识别语音
2019/11/22 Javascript
高效测试用例组织算法pairwise之Python实现方法
2017/07/19 Python
Python matplotlib 画图窗口显示到gui或者控制台的实例
2018/05/24 Python
python实现文本界面网络聊天室
2018/12/12 Python
python实现自动化上线脚本的示例
2019/07/01 Python
django中使用Celery 布式任务队列过程详解
2019/07/29 Python
YUV转为jpg图像的实现
2019/12/09 Python
Python通过2种方法输出带颜色字体
2020/03/02 Python
python中tab键是什么意思
2020/06/18 Python
详解matplotlib中pyplot和面向对象两种绘图模式之间的关系
2021/01/22 Python
HTML5 canvas基本绘图之文字渲染
2016/06/27 HTML / CSS
MyHeritage美国:家族史研究和DNA测试的领先服务
2019/05/27 全球购物
YSL圣罗兰美妆俄罗斯官网:Yves Saint Lauret RU
2020/09/23 全球购物
奥林匹亚体育:Olympia Sports
2020/12/30 全球购物
接口中的方法可以是abstract的吗
2015/07/23 面试题
在Ajax应用中信息是如何在浏览器和服务器之间传递的
2016/05/31 面试题
工厂保洁员岗位职责
2013/12/04 职场文书
西安交大自主招生自荐信
2014/01/27 职场文书
委托书范本
2014/04/02 职场文书
维修工先进事迹
2014/05/29 职场文书
应届生自荐信
2014/06/30 职场文书
班子群众路线教育实践个人对照检查材料思想汇报
2014/09/30 职场文书
工会经费申请报告
2015/05/15 职场文书
远程教育集中轮训基层干部培训班学习心得体会
2016/01/09 职场文书
励志语录:时光飞逝,请学会珍惜所有的人和事
2020/01/16 职场文书
python如何在word中存储本地图片
2021/04/07 Python