自定義Angular指令與jQuery實現(xiàn)的Bootstrap風格數(shù)據(jù)雙向綁定的單選與多選下拉框
先說點閑話,熟悉Angular的猿們會喜歡這個插件的。
00.本末倒置
不得不承認我是一個喜歡本末倒置的人,學生時代就喜歡先把晚交的作業(yè)先做,留著馬上就要交的作業(yè)不做,然后慢悠悠做完不重要的作業(yè),臥槽,XX作業(yè)馬上要交了,趕緊補補補。如今做這個項目,因為沒找到合適的多選下拉Web插件,又不想用html自帶的丑陋的<select multiple></select>,自己花了一整天時間做了一個?;蛟S這樣占用的主要功能開發(fā)的時間,開發(fā)起來會更有緊迫感吧。感覺自己是個抖M自虐傾向,并且伴有css和代碼縮進強迫癥的程序猿。
01.畫蛇添足
Angular強大的控制器似乎已經(jīng)可以滿足大部分UI上的需求了,但是NodeJS應用往往會使用ejs,jade這樣的模板引擎來動態(tài)生成html頁面,那么問題來了,當我想把后臺傳給express中res.render()的參數(shù)直接顯示到界面而且綁定到相應的ng-model怎么辦?
解決方法1,不要什么事一次來,Angular的Controller發(fā)個post請求再拿數(shù)據(jù)不就行了
解決方法2,先用模板暫存在html上,再讓Controller根據(jù)頁面上的數(shù)據(jù)來初始化$scope的值
解決方法3,鄙人對Angular和EJS才疏學淺,誰有好辦法教我唄
比如現(xiàn)在要做一個選擇下拉框<select>n個<option>xx</option></select>,選項在后臺,我不想單獨發(fā)post拿,也不想放在頁面上,Controller單獨寫邏輯處理,而Angular社區(qū)有個ui-select插件,看起來數(shù)據(jù)是從$scope取的,并不是直接拿的<option />標簽的數(shù)據(jù),當時我就火了,不就一個下拉框,自己做唄。
10.樂觀的程序猿
思路很明確,定義一個Angular directive -> 把選項值拿出來 -> 各種事件加加加 -> scope數(shù)據(jù)綁定 -> 完結(jié)撒花
我估計的時間是半天,然而實際花了多久只能呵呵了,css強迫癥,Angular理解不深(所以很多html操作還是在用jQuery),事件考慮不全導致了最終花了超過兩倍的時間做完,
不廢話了,簡單實用,既可以即時綁定ng-model $scope.xxx,也可以直接調(diào)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示例的規(guī)范定義選擇框就行啦,是不是很有html原生select標簽的親切感
2.源碼解釋:dom操作和事件都是用jQuery實現(xiàn)的,每一步都有簡略的注釋,實現(xiàn)雙向綁定的關鍵在于取得標簽上定義的ng-model,然后在事件中設置scope[ng-model]的值,
并且調(diào)用$digest()循環(huán)來讓Angular根據(jù)ng-model更新DOM,$digest是Angular實現(xiàn)雙向綁定的核心之一,原理是將變化的scope值同步到所有需要更新的地方,實現(xiàn)暫時還不大明白,有空單獨研究一下這些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上一起完善吧,自定義選項模板,選項分組這兩個功能還沒有實現(xiàn)。少年,加入開源的大軍吧。
以上所述是小編給大家分享的自定義Angular指令與jQuery實現(xiàn)的Bootstrap風格數(shù)據(jù)雙向綁定的單選與多選下拉框,希望大家喜歡。
- BootStrap中關于Select下拉框選擇觸發(fā)事件及擴展
- Bootstrap模塊dropdown實現(xiàn)下拉框響應
- Bootstrap框架下下拉框select搜索功能
- Bootstrap select多選下拉框?qū)崿F(xiàn)代碼
- bootstrap datetimepicker實現(xiàn)秒鐘選擇下拉框
- Bootstrap select實現(xiàn)下拉框多選效果
- Bootstrap實現(xiàn)彈性搜索框
- 使用Bootstrap typeahead插件實現(xiàn)搜索框自動補全的方法
- Bootstrap3制作搜索框樣式的方法
- 基于bootstrap實現(xiàn)多個下拉框同時搜索功能
相關文章
angular6 利用 ngContentOutlet 實現(xiàn)組件位置交換(重排)
這篇文章主要介紹了angular6 利用 ngContentOutlet 實現(xiàn)組件位置交換(重排),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11
angularJs使用$watch和$filter過濾器制作搜索篩選實例
本篇文章主要介紹了angularJs使用$watch和$filter過濾器制作搜索篩選實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
AngularJS基礎 ng-readonly 指令簡單示例
本文主要介紹AngularJS ng-readonly 指令,這里對ng-readonly指令的資料做了整理,有學習AngularJS 指令的同學可以參考下2016-08-08
Angular.JS判斷復選框checkbox是否選中并實時顯示
最近因為工作需要做了一個選擇標簽的功能,把一些標簽展示給用戶,用戶選擇自己喜歡的標簽,就類似我們在購物網(wǎng)站看到的那種過濾標簽似的,所以這篇文章就給大家介紹了Angular.JS判斷復選框checkbox是否選中并實時顯示的方法,下面來一起看看吧。2016-11-11

