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 isArray 数组类型检测函数
Oct 08 Javascript
node.js中的fs.rename方法使用说明
Dec 16 Javascript
EasyUI实现第二层弹出框的方法
Mar 01 Javascript
jquery右下角自动弹出可关闭的广告层
May 08 Javascript
JavaScript构造函数详解
Dec 27 Javascript
jQuery事件委托之Safari
Jul 05 Javascript
表单元素值获取方式js及java方式的简单实例
Oct 15 Javascript
jQuery File Upload文件上传插件使用详解
Dec 06 Javascript
详解js前端代码异常监控
Jan 11 Javascript
微信小程序 自定义Toast实例代码
Jun 12 Javascript
vue实现导航栏效果(选中状态刷新不消失)
Dec 13 Javascript
JS多个表单数据提交下的serialize()应用实例分析
Aug 27 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
destoon会员注册提示“数据校验失败(2)”解决方法
2014/06/21 PHP
PHP+Mysql+jQuery文件下载次数统计实例讲解
2015/10/10 PHP
php计划任务之验证是否有多个进程调用同一个job的方法
2015/12/07 PHP
修改WordPress中文章编辑器的样式的方法详解
2015/12/15 PHP
php 使用curl模拟ip和来源进行访问的实现方法
2017/05/02 PHP
PHP实现的简单对称加密与解密方法实例小结
2017/08/28 PHP
深入理解PHP的远程多会话调试
2017/09/21 PHP
Yii2框架操作数据库的方法分析【以mysql为例】
2019/05/27 PHP
js实现的简单图片浮动效果完整实例
2016/05/10 Javascript
JavaScript实现DOM对象选择器
2016/09/24 Javascript
canvas实现手机端用来上传用户头像的代码
2016/10/20 Javascript
jquery实现文字单行横移或翻转(上下、左右跳转)
2017/01/08 Javascript
javascript监听页面刷新和页面关闭事件方法详解
2017/01/09 Javascript
mui上拉加载更多下拉刷新数据的封装过程
2017/11/03 Javascript
浅谈vue项目重构技术要点和总结
2018/01/23 Javascript
vue插件开发之使用pdf.js实现手机端在线预览pdf文档的方法
2018/07/12 Javascript
Vue实现6位数密码效果
2018/08/18 Javascript
vue 移动端适配方案详解
2018/11/15 Javascript
vue抽出组件并传值实例
2020/07/31 Javascript
django rest framework 数据的查找、过滤、排序的示例
2018/06/25 Python
Numpy之random函数使用学习
2019/01/29 Python
python多继承(钻石继承)问题和解决方法简单示例
2019/10/21 Python
美国在线眼镜商城:Eyeglasses.com
2017/06/26 全球购物
实习生自荐信范文
2013/11/13 职场文书
应届毕业生求职信范文
2013/12/18 职场文书
六查六看六改心得体会
2014/10/14 职场文书
2014年语文教学工作总结
2014/12/17 职场文书
教师个人事迹材料
2014/12/17 职场文书
餐厅收银员岗位职责
2015/04/07 职场文书
经营目标责任书
2015/05/08 职场文书
2015年庆祝国庆节66周年演讲稿
2015/07/30 职场文书
生活小常识广播稿
2015/08/19 职场文书
医院中层管理人员培训心得体会
2016/01/11 职场文书
世界十大儿童漫画书排名,法国国宝漫画排第五,第二是轰动日本连环
2022/03/18 欧美动漫
动画「半妖的夜叉姬」新BD特典图公开
2022/03/22 日漫
mysql字段为NULL索引是否会失效实例详解
2022/05/30 MySQL