先说点闲话,熟悉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风格数据双向绑定的单选与多选下拉框,希望大家喜欢。

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

  1. HTML5、Select下拉框右边加图标的实现代码(增进用户体验)

    这篇文章主要介绍了HTML5、Select下拉框右边加图标的实现代码,深度美化页面增进用户体验效果,需要的朋友可以参考下

  2. Android Studio是否支持用于Android UI设计的AngularJS?

    我对AndroidStudio有疑问:AS在设计XML文件时是否支持AngularJS代码,例如:对于小动画或效果?

  3. android – 如何使用ClientID和ClientSecret在Phonegap中使用Angularjs登录Google OAuth2

    我正尝试使用Angularjs(使用IonicFramework)通过GoogleOAuth2从我的Phonegap应用程序登录.目前我正在使用http://phonegap-tips.com/articles/google-api-oauth-with-phonegaps-inappbrowser.html进行登录.但是当我使用Angular-UI-RouterforIonic时,它正在创建非常

  4. AngularJS中下拉框的高级用法示例

    这篇文章主要介绍了AngularJS中下拉框的高级用法,结合实例形式分析了AngularJS下拉框的遍历、选择、绑定、显示等功能实现方法,需要的朋友可以参考下

  5. 利用require.js与angular搭建spa应用的方法实例

    这篇文章主要给大家介绍了关于利用require.js与angular搭建spa应用的方法实例,文中通过示例代码给大家介绍的非常详细,对大家的理解和学习具有一定的参考学习价值,需要的朋友们下面跟着小编来一起看看吧。

  6. 详解Angular动态组件

    本文主要介绍了Angular动态组件,对此感兴趣的同学,可以亲自实验一下。

  7. 详解如何使用webpack+es6开发angular1.x

    本篇文章主要介绍了详解如何使用webpack+es6开发angular1.x,具有一定的参考价值,有兴趣的可以了解一下

  8. angular2系列之路由转场动画的示例代码

    本篇文章主要介绍了angular2系列之路由转场动画的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  9. 一种angular的方法级的缓存注解(装饰器)

    本篇文章主要介绍了一种angular的方法级的缓存注解(装饰器),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. 动手写一个angular版本的Message组件的方法

    本篇文章主要介绍了动手写一个angular版本的Message组件的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部