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 相关文章推荐
alixixi runcode.asp的代码不错的应用
Aug 08 Javascript
jquery提交form表单时禁止重复提交的方法
Feb 13 Javascript
基于JavaScript实现移动端点击图片查看大图点击大图隐藏
Nov 04 Javascript
Javascript之String对象详解
Jun 08 Javascript
Jquery与Bootstrap实现后台管理页面增删改查功能示例
Jan 22 Javascript
原生js实现瀑布流布局
Mar 08 Javascript
使用node打造自己的命令行工具方法教程
Mar 26 Javascript
学习node.js 断言的使用详解
Mar 18 Javascript
微信小程序城市选择及搜索功能的方法
Mar 22 Javascript
jQuery实现可编辑的表格
Dec 11 jQuery
js实现验证码功能
Jul 24 Javascript
Vue单文件组件开发实现过程详解
Jul 30 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
德生S2000南麂列岛台湾FM收听记录
2021/03/02 无线电
一个取得文件扩展名的函数
2006/10/09 PHP
获得Google PR值的PHP代码
2007/01/28 PHP
PHP开发规范手册之PHP代码规范详解
2011/01/13 PHP
javascript+php实现根据用户时区显示当地时间的方法
2015/03/11 PHP
ThinkPHP、ZF2、Yaf、Laravel框架路由大比拼
2015/03/25 PHP
合格的PHP程序员必备技能
2015/11/13 PHP
JavaScript 监听textarea中按键事件
2009/10/08 Javascript
TreeView 用法(有代码)(asp.net)
2011/07/15 Javascript
jQuery中对节点进行操作的相关介绍
2013/04/16 Javascript
JS操作图片(增,删,改) 例子
2013/04/17 Javascript
Javascript Web Slider 焦点图示例源码
2013/10/10 Javascript
JQuery实现鼠标滚轮滑动到页面节点
2015/07/28 Javascript
jquery输入数字随机抽奖特效的简单实现代码
2016/06/10 Javascript
AngularJS 使用ng-repeat报错 [ngRepeat:dupes]
2017/01/19 Javascript
为你的微信小程序体积瘦身详解
2017/05/20 Javascript
基于Vue.js实现tab滑块效果
2017/07/23 Javascript
react-native中ListView组件点击跳转的方法示例
2017/09/30 Javascript
JS/HTML5游戏常用算法之碰撞检测 地图格子算法实例详解
2018/12/12 Javascript
Vue响应式原理Observer、Dep、Watcher理解
2019/06/06 Javascript
[02:35]DOTA2超级联赛专访XB 难忘一年九冠称王
2013/06/20 DOTA
[03:37]2016完美“圣”典 风云人物:Mikasa专访
2016/12/07 DOTA
深入理解Python3中的http.client模块
2017/03/29 Python
Python实现的微信支付方式总结【三种方式】
2019/04/13 Python
python实现列表中最大最小值输出的示例
2019/07/09 Python
解决django接口无法通过ip进行访问的问题
2020/03/27 Python
使用Django实现把两个模型类的数据聚合在一起
2020/03/28 Python
如何用Python徒手写线性回归
2021/01/25 Python
纯css3实现效果超级炫的checkbox复选框和radio单选框
2014/09/01 HTML / CSS
Html5实现单张、多张图片上传功能
2019/04/28 HTML / CSS
李维斯牛仔裤英国官方网站:Levi’s英国
2019/10/10 全球购物
一套C#面试题
2013/10/09 面试题
幼儿园教师辞职信
2014/01/18 职场文书
终止或解除劳动合同及劳动关系的证明书
2014/10/06 职场文书
2014年物业公司工作总结
2014/11/22 职场文书
违反工作规定检讨书范文
2014/12/14 职场文书