自定义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模块,像这样 ↓
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日期时间插件datetimepicker实现双向绑定
一放假就高产似母猪了. 00.混乱的前端界 Angular1.x确实是个学习成本很高的框架,刚开始实习那会儿,前端啥也不懂,工头说用Angular,我们这群小弟也只能硬着头皮学.在这之前,前端的东西大 ...
- js:jquery multiSelect 多选下拉框实例
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- pentaho cde 自定义复选下拉框 checkbox select
pentaho 自带的component 虽多,但是当用户需要在一个表格中查看多个组别的数据时,pentaho自带的单选框就不能实现了,所以复选下拉框势在必行,实现效果如下: 实现原理是借用了jqu ...
- 自定义SWT控件二之自定义多选下拉框
2.自定义下拉多选框 package com.view.control.select; import java.util.ArrayList; import java.util.HashMap; im ...
- MVC身份验证.MVC过滤器.MVC6关键字Task,Async.前端模拟表单验证,提交.自定义匿名集合.Edge导出到Excel.BootstrapTree树状菜单的全选和反选.bootstrap可搜索可多选可全选下拉框
1.MVC身份验证. 有两种方式.一个是传统的所有控制器继承自定义Control,然后再里面用MVC的过滤器拦截.所以每次网站的后台被访问时.就会先走入拦截器.进行前端和后端的验证 一个是利用(MVC ...
- jQuery自定义多选下拉框
项目中需要自定义一个下拉框多选插件,业务问题还是自己实现比较好 通过$.fn 向jQuery添加新的方法 下拉数据通过参数传递进去,通过调用该插件时接收,选择后的确定与取消事件采用事件传递方式 代码如 ...
- jQuery UI 多选下拉框插件:jquery-ui-multiselect
前一个项目,由于项目需求,需要大量使用到下拉多选框,而由于本人又不会写有关 CSS 样式,所以,便上网找到了这个 jQuery 插件:jquery-ui-multiselect .该款插件提供了基本下 ...
- jquery.multiselect 多选下拉框实现
第一步:链接下列文件,如果没有,到此网页下载 https://github.com/ehynds/jquery-ui-multiselect-widget,此插件基于jquery ,所以jquery的 ...
- 【Bootstrap】Bootstrap-select多选下拉框实现
目录 前言 需要引用的它们 核心选项 核心方法 实例应用 回到顶部 前言 项目中要实现多选,就想到用插件,选择了bootstrap-select. 附上官网api链接,http://silviomor ...
随机推荐
- Redis 命令 - Sorted Sets
ZADD key score member [score member ...] Add one or more members to a sorted set, or update its scor ...
- Nginx - Core Module Directives
The following is the list of directives made available by the Core module. Most of these directives ...
- c#输出json,其中包含子json (可以含 无限级 子json)的方法思路
首页 给出 DataTable 转Json 的方法: public static string TableToJson(DataTable dt) { List<Dictionary<s ...
- 关于 angular 小心得
心得1: //控制器里面的代码会晚一些执行 setTimeout(function(){ //获取对象的scope var ele = document.querySelector('[ng-cont ...
- eclipse中英文切换--四种方式
若转载,请注明出处 http://www.cnblogs.com/last_hunter/p/5627009.html 谢谢! ------------------------------------ ...
- GIS初学者
学习编程一直以来没有什么好的思路,感觉就是学了忘,忘了再重复,效率特别低下.大概是从大三第一学期才有意识的转向c#的学习,来熟悉VS2010平台,在这之前我都不知道自己是怎么学习的. 大一第二学期开的 ...
- Qt 串口通信
在Qt5之前,串口通信基本依赖于第三方库,下面是我曾接触过的串口通信类库: 名称 语言 平台 QextSerialPort QT C++ Win/Linux http://sourceforge. ...
- 指针与strncpy---内存
指针的形式的赋值和strncpy的赋值 e.SetAttr("Amt", ToString(dAmt) ); e.SetAttr("Amt", sAm ...
- Windows内存管理[转]
本文主要内容:1.基本概念:物理内存.虚拟内存:物理地址.虚拟地址.逻辑地址:页目录,页表2.Windows内存管理3.CPU段式内存管理4.CPU页式内存管理 一.基本概念1. 两个内存概念物理内存 ...
- linux FTP 批量下载文件
wget是一个从网络上自动下载文件的自由工具,支持通过HTTP.HTTPS.FTP三个最常见的TCP/IP协议下载,并可以使用HTTP代理.wget名称的由来是“World Wide Web”与“ge ...