自定义Angular指令与jQuery实现的Bootstrap风格数据双向绑定的单选与多选下拉框

先说点闲话,熟悉Angular的猿们会喜欢这个插件的。

00.本末倒置

不得不承认我是一个喜欢本末倒置的人,学生时代就喜欢先把晚交的作业先做,留着马上就要交的作业不做,然后慢悠悠做完不重要的作业,卧槽,XX作业马上要交了,赶紧补补补。如今做这个项目,因为没找到合适的多选下拉Web插件,又不想用html自带的丑陋的<select multiple></select>,自己花了一整天时间做了一个。或许这样占用的主要功能开发的时间,开发起来会更有紧迫感吧。感觉自己是个抖M自虐倾向,并且伴有css和代码缩进强迫症的程序猿。

01.画蛇添足

Angular强大的控制器似乎已经可以满足大部分UI上的需求了,但是NodeJS应用往往会使用ejs,jade这样的模板引擎来动态生成html页面,那么问题来了,当我想把后台传给express中res.render()的参数直接显示到界面而且绑定到相应的ng-model怎么办?

解决方法1,不要什么事一次来,Angular的Controller发个post请求再拿数据不就行了

解决方法2,先用模板暂存在html上,再让Controller根据页面上的数据来初始化$scope的值

解决方法3,鄙人对Angular和EJS才疏学浅,谁有好办法教我呗

比如现在要做一个选择下拉框<select>n个<option>xx</option></select>,选项在后台,我不想单独发post拿,也不想放在页面上,Controller单独写逻辑处理,而Angular社区有个ui-select插件,看起来数据是从$scope取的,并不是直接拿的<option />标签的数据,当时我就火了,不就一个下拉框,自己做呗。

10.乐观的程序猿

思路很明确,定义一个Angular directive -> 把选项值拿出来 -> 各种事件加加加 -> scope数据绑定 -> 完结撒花
我估计的时间是半天,然而实际花了多久只能呵呵了,css强迫症,Angular理解不深(所以很多html操作还是在用jQuery),事件考虑不全导致了最终花了超过两倍的时间做完,
不废话了,简单实用,既可以即时绑定ng-model $scope.xxx,也可以直接调jQuery的$("标签的id").val()也能拿到值,
git传送门duang:https://git.oschina.net/code2life/easy-select.git
demo传送门duang~duang:http://ydxxwb.sinaapp.com/easy-select-demo/  (代码不是最新,有两个fix的bug还没有部署上去)

11.放码

1.使用方法: 引入库文件Bootstrap,Angular1.x,引入style.css文件(可以修改css自定义自己想要的样式),easy-select.js,定义Angular的Controller,依赖easySelect模块,像这样 ↓
angular.module('dataDisplay', ['easySelect']).controller('selectController', ['$scope', '$http',function ($scope, $http) {  // your code }]);

然后参考demo示例的规范定义选择框就行啦,是不是很有html原生select标签的亲切感

2.源码解释:dom操作和事件都是用jQuery实现的,每一步都有简略的注释,实现双向绑定的关键在于取得标签上定义的ng-model,然后在事件中设置scope[ng-model]的值,
并且调用$digest()循环来让Angular根据ng-model更新DOM,$digest是Angular实现双向绑定的核心之一,原理是将变化的scope值同步到所有需要更新的地方,实现暂时还不大明白,有空单独研究一下这些Angular里面$,$$开头的东西。

3.自适应与css,Bootstrap就是自适应的,css可以自己定制不同的风格,style.css都有相关注释

easy-select.js

var comDirective = angular.module('easySelect', []);
comDirective.directive("easySelect", function () {
 return {
  link: function (scope, element, attrs) {
   var ngModel = $(element).attr("ng-model");
   if(!ngModel || ngModel.length == 0) {
    ngModel = "defaultSelectModel";
   }
   var status = false; //toggle boolean
   var valueMap = "";
   var options = $(element).children();
   $(element).attr("style", "padding:0");
   //hide original options
   $.each(options, function (opt) {
    $(options[opt]).attr("style", "display:none");
   });
   //build ul
   var html = "<div id='" + attrs.id + "-root' style='width:100%;position: relative;left:-1px'>" +
    "<p id='display-"+attrs.id + "' style='padding:6px 12px "+ ((attrs.multiple != undefined)?"4px":"7px")+
    " 12px;margin:0;border:none;width:95%;margin-left:2px;background-color: transparent'>" +
    "<span style='display: inline-block;padding-bottom: 3px'> </span></p>" + //this is a dummy span
    "<ul id='" + attrs.id +
    "-container' class='list-group easy-select-container' style='display:none'>"; //options' container
   if(attrs.multiple != undefined) {
    $.each(options, function (opt) {
     html += "<li value='"+ $(options[opt]).val() +"' class='my-li-container list-group-item option-"+
     attrs.id+ "'><div style='width:100%;display:inline-block'>" + $(options[opt]).html() +
     "</div><span value='"+ $(options[opt]).val() +"' class='my-li-option glyphicon glyphicon-ok'></span></li>";
    });
   } else {
    $.each(options, function (opt) {
     if($(options[opt]).attr("default") != undefined) {
      scope[ngModel] = $(options[opt]).val();
      valueMap = $(options[opt]).html();
      html += "<li value='"+ $(options[opt]).val() +"' class='my-li-container list-group-item option-"+ attrs.id+ "'>"
      + $(options[opt]).html() + "</li>";
     } else {
      html += "<li value='"+ $(options[opt]).val() +"' class='my-li-container list-group-item option-"+ attrs.id+ "'>"
      + $(options[opt]).html() + "</li>";
     }
    });
   }
   //if multiple, add button
   if (attrs.multiple != undefined) {
    html += "<li class='list-group-item ' for='ensure-li'><button class='btn btn-default'" +
    " for='ensure-btn' style='padding: 2px' > 确定 </button></li>";
   }
   //render ui
   html += "</ul></div>";
   $(element).append(html);
   $(".my-li-option").each(function(){
    $(this).fadeOut(0);
   });
   if(attrs.multiple == undefined)
    $($("#display-"+attrs.id).children()[0]).html(valueMap);
   //adjust width
   $("#" + attrs.id + "-root").width($("#" + attrs.id + "-root").width() + 2);
   //mouse leave event
   $(element).mouseleave(function(){
    $(".my-li-container").each(function(){
     $(this).attr("style","");
    });
    if(status) {
     $("#" + attrs.id + "-container").attr("style", "display:none");
     status = !status;
    }
   });
   //multiple select seems complex
   if (attrs.multiple != undefined) {
    //click event
    $(element).click(function (e) {
     //if click on tags, remove it
     if($(e.target).attr("for") == "option-tag") {
      // change val and digest change item in angular
      scope[ngModel] = $(element).val().replace($(e.target).attr("value"),"").replace(/;+/,";").replace(/^;/,"");
      $(element).val(scope[ngModel]);
      scope.$digest();
      $(e.target).remove();
      $(".my-li-option").each(function(){
       if($(this).attr("value") == $(e.target).attr("value")) {
        $(this).css("opacity","0.01");
       }
      });
     } else if($(this).attr("for") != 'ensure-li') {
      //toggle ul
      $("#" + attrs.id + "-container").attr("style", status ? "display:none" : "");
      status = !status;
     }
    });
    $(".option-"+attrs.id).each(function(){
     $(this).on('click',function(){
      var selectValue = $(element).val();
      var currentValue = $(this).attr("value");
      var selected = false;
      //if option is selected ,remove it
      var temp = selectValue.split(";");
      $.each(temp,function(obj){
       if(temp[obj].indexOf(currentValue) != -1) {
        selected = true;
       }
      })
      if(selected) {
       $($(this).children()[1]).fadeTo(300,0.01);
       scope[ngModel] = $(element).val().replace(currentValue,"").replace(/;{2}/,";").replace(/^;/,"");
       $(element).val(scope[ngModel]);
       scope.$digest();
       $("#display-"+attrs.id + " span").each(function(){
        if($(this).attr("value") == currentValue) {
         $(this).remove();
        }
       });
      } else {
       //add option to val() and ui
       $($(this).children()[1]).fadeTo(300,1);
       scope[ngModel] = ($(element).val()+";"+currentValue).replace(/;{2}/,";").replace(/^;/,"");
       $(element).val(scope[ngModel]);
       scope.$digest();
       $("#display-"+attrs.id).append(
        "<span for='option-tag' value='"+ $(this).attr("value") +"' class='p-option-tag'>"
        +$(this).children()[0].innerHTML+ "</span>");
      }
      status = !status; // prevent bubble
     });
     //control background
     $(this).mouseenter(function(){
      $(".my-li-container").each(function(){
       $(this).attr("style","");
      });
      $(this).attr("style","background-color:#eee");
     });
    });
   } else {
    $(".option-"+attrs.id).each(function(){
     $(this).mouseenter(function(){
      $(".my-li-container").each(function(){
       $(this).attr("style","");
      });
      $(this).attr("style","background-color:#eee");
     });
    });
    //single select ,just add value and remove ul
    $(element).click(function () {
     $("#" + attrs.id + "-container").attr("style", status ? "display:none" : "");
     status = !status;
    });
    $(".option-"+attrs.id).each(function(){
     $(this).on('click',function(){
      scope[ngModel] = $(this).attr("value");
      $(element).val(scope[ngModel]);
      scope.$digest();
      console.log(ngModel);
      console.log(element.val());
      $($("#display-"+attrs.id).children()[0]).html($(this).html());
     });
    });
   }
  }
 }
}); 

100.如果看到了这里,说明对这个小东西有兴趣,git上一起完善吧,自定义选项模板,选项分组这两个功能还没有实现。少年,加入开源的大军吧。

以上所述是小编给大家分享的自定义Angular指令与jQuery实现的Bootstrap风格数据双向绑定的单选与多选下拉框,希望大家喜欢。

时间: 2015-12-11

使用Bootstrap typeahead插件实现搜索框自动补全的方法

这就是贴代码的坏处之一:搜索框快被网友玩儿坏了!!!有故意输入空格的,有输入or 1=1的,有alert的,有html乱入的.......而且好像还在玩儿,随他们去吧,只要开心就好. 在项目中,经常会用到输入框的自动补全功能,就像百度.淘宝等搜索框一样:当用户输入首字母.关键词时,后台会迅速将与此相关的条目返回并显示到前台,以便用户选择,提升用户体验.当然本项目的补全功能和这些大厂的技术是没有可比性的,但用于站内搜索也是绰绰有余了. 接触到的自动补全插件主要有两个:autocomplete和ty

Bootstrap模块dropdown实现下拉框响应

本文介绍了Bootstrap下拉框模块dropdown的使用方法,供大家参考,具体内容如下 一.源码分析: Dropdowns.scss:下拉框模块 Javascripts/bootstrap/dropdown.js:实现下拉框响应 二.功能及原理: 下拉选项卡,默认不能实现显示选中项的功能 原理: 1.利用dropdown类作为定位点,然后让子级的列表dropdown-menu绝对定位实现,还需要加一个单击点作为设置data-toggle="dropdown"才能做关联. 2. 需要

基于bootstrap实现多个下拉框同时搜索功能

本文实例为大家分享了bootstrap实现下拉框搜索展示的具体代码,供大家参考,具体内容如下 1.第一个下拉框代码 <div class="btn-group col-sm-3 col-xs-6 util-btn-margin-bottom-5 quick-search"> <!--快速查询--> <div class="input-group"> <span class="input-group-addon&qu

Bootstrap select多选下拉框实现代码

前言 项目中要实现多选,就想到用插件,选择了bootstrap-select. 附上官网api链接,http://silviomoreto.github.io/bootstrap-select/. 没有中文的.对付看吧.有机会每个方法实践一下,会总结的.我自己也等着呢. 需要引用的它们 <!-- Latest compiled and minified CSS --> <link rel="stylesheet" href="https://cdnjs.cl

BootStrap中关于Select下拉框选择触发事件及扩展

Select下拉框的问题,想在选择一个选项后,前台显示做出变动,并且知道选择的是第几个选项. 这个很好解决: 如下: <div class="page-header"> <div class="form-horizontal"> <div class="control-label col-lg-0"> </div> <div class="col-lg-2"> <

bootstrap datetimepicker实现秒钟选择下拉框

bootstrap datetimepicker插件没有秒钟选择器,如果要想选择的时间精确到秒没有办法控制,虽然可以配置format:'yyyy-mm-dd hh:ii:ss',会将秒钟添加到输入框中,但是无法控制秒钟数值,默认为当前客户端的时间的秒钟. 本示例修改bootstrap datetimepicker源代码,如果配置了显示秒钟format:'......ss',将会给分钟选择器层添加秒钟选择下拉框,可以自定义选择时间的秒钟部分,效果如下 bootstrap datetimepicke

Bootstrap实现弹性搜索框

Bootstrap是Twitter推出的一个用于前端开发的开源工具包.它由Twitter的设计师Mark Otto和Jacob Thornton合作开发,是一个CSS/HTML框架. 接下来通过本文给大家介绍Bootstrap实现弹性搜索框的代码,一起看下吧. <form action="" method="get" class="form-horizontal"> <div class="input-group se

Bootstrap select实现下拉框多选效果

在学习bootstrap实现下拉多选效果的时候,觉得该效果很好,所以拿来分享下,这里就不详细的描述了,直接附上代码给各位看看 HTML代码: <div class= "row" style ="margin-top :10px;"> <div class= "form-group col-xs-12"> <label for= "sceneModel_title" class="col-

Bootstrap3制作搜索框样式的方法

Bootstrap3中的很多样式,都是需要我们进行摸索着学习的,在制作类似百度的素搜索框时,一开始会不知道怎么做,其实很简单,利用内联图标方式就可以实现 <div class="input-group"> <input type="text" class="form-control input-lg"><span class="input-group-addon btn btn-primary"&

Bootstrap框架下下拉框select搜索功能

之前用Easyui框架下做的下拉框选择,虽然可以搜索,但是有诸多的不便:比如,不能模糊匹配,必须按照第一个字母来搜索 心血来潮,想换个框架写后台,然后用Bootstrap搭了个架子,然后两种样式冲突,百度了一把,找到了另一个牛逼的基于Bootstrap下拉框搜索功能的js 不区分大小写,模糊匹配,哈哈,太棒了~~ 具体用法: <script type="text/javascript" src="<%=basePath%>/js/commons/jquery

yii2组件之下拉框带搜索功能的示例代码(yii-select2)

简单的小功能,但是用起来还是蛮爽的.分享出来让更多的人有更快的开发效率,开开心心快乐编程. 如果你还没有使用过composer,你可就out了,看我的教程分享,composer简直就是必备神奇有木有.都说到这个点上了,我们赶紧使用composer进行安装吧. 不急,先来看看效果图是啥样的,不然都没心情没欲望看下去. 啥玩意,不感兴趣?继续看嘛,看完再操作一边才能觉得好在哪里. 有木有感觉很帅气,当然啦,远远不止,还很上档次用起来效果也是杠杠的有木有. 好了好了,抓紧时间安装,不然聊起来真是没完没

使用bootstrap实现下拉框搜索功能的实例讲解

背景 公司的小二后台有一个下拉框选择经销商的功能,由于选择的经销商过多,因此添加搜索功能. 前提 配置好Bootstrap相关的开发环境 主要内容 <div class="form-group"> <label class="col-sm-3 control-label" for="state">经销商信息</label> <div class="col-sm-3"> <s

bootstrap实现多个下拉框同时搜索的实例

1.第一个下拉框代码 <div class="btn-group col-sm-3 col-xs-6 util-btn-margin-bottom-5 quick-search"> <!--快速查询--> <div class="input-group"> <span class="input-group-addon"><i class="fa fa-search">

利用Bootstrap Multiselect实现下拉框多选功能

利用Bootstrap Multiselect实现下拉框多选功能,并在点击事件中获取到所有选中option的value值 首先展示项目案例: 多选下拉框功能实现.gif 下面是所有完整的代码,重点以及主要的解释已经在代码内进行注释: 引入的css.js文件需要从github中下载:https://github.com/davidstutz/bootstrap-multiselect 插件依赖jQ和bootstrap,所以在引入插件之前需要先引入这jQ和bootstrap的css和js文件. <!

js实现Select下拉框具有输入功能的方法

本文实例讲述了js实现Select下拉框具有输入功能的方法.分享给大家供大家参考.具体实现方法如下: 实现方法一 复制代码 代码如下: <HTML> <HEAD> <META http-equiv='Content-Type' content='text/html; charset=gb2312'> <TITLE>js实现可输入的下拉框</TITLE> </HEAD> <BODY> <div style="

Select下拉框模糊查询功能实现代码

select下拉列表框支持输入模糊查询功能,设计非常的人性化,下面小编给大家分享了关键代码,大家根据自己的需求适当的添加修改,有任何问题欢迎给我反馈. 关键代码如下所示: <HTML> <HEAD> <META http-equiv='Content-Type' content='text/html; charset=gb2312'> <TITLE>可输入的下拉框</TITLE> </HEAD> <BODY > <S

Angular实现下拉框模糊查询功能示例

本文实例讲述了Angular实现下拉框模糊查询功能.分享给大家供大家参考,具体如下: 前两天研究了一下angularjs,不得不说angularjs的mvc思想还是很强大的.对应偏重于数据处理的项目还是非常有优势的. 写了个搜索下拉框的demo,注释在里边都写了,就不再这罗嗦了. 1. 普通方式实现 <!DOCTYPE html> <html> <head lang="zh_CN"> <meta charset="utf-8"

jQuery实现下拉框选择图片功能实例

本文实例讲述了jQuery实现下拉框选择图片功能.分享给大家供大家参考.具体如下: 让下拉框中显示图片,并可选择对应图片,让select下拉框不仅可显示文字,还可以显示图片内容.为了更生动些,这里还加入了jQuery动画效果,当展开Select列表的时候,图片渐变显示.使用了一个jQ插件:imageselect.js,使用效果的朋友可以自己下载吧. 运行效果截图如下: 具体代码如下: <!DOCTYPE html> <head> <title>支持图片选择的jQuery