angular用了有一段时间了,基本的使用问题已经很熟练了,但是angular组件的缺乏让开发的时候比较头疼,没有合适的组件只能妥协,使用基本的使用方法去实现本来已经设计好的比较好的交互体验,这样的结果就是交互体验打了折扣。
言归正传,这次的自定义组件的目标是弹出框,之所以要研究弹出框是因为弹出框应该算是比较有代表性的组件之一,需要处理的问题包含定位、数据传递、DOM操作、事件处理等等,把这个搞定了,基本做其它的各类组件开发可以上手了。
终极目标达到类似下面的效果,要求:弹出框内容可定义,组件位置与事件触发源位置关联。
下面开工
阶段目标:
本篇要解决的问题是弹出相应的层,并解决定位问题、大小问题、内容填充的自定义、鼠标背景事件(鼠标在其它地方点击的时候自动关闭弹出框)问题。
step1:定义内个ng-template
//定义popbox的父容器
<script type="text/ng-template" id="popbox.html">
<div class='popbox'>
</div>
</script>
//定义内容模板,随便放了点东西
<script type="text/ng-template" id="labels.html">
<h3>labels</h3>
</script>
//定义另外一个内容模板,用来测试popbox自定义内容填充
<script type="text/ng-template" id="labels1.html">
<h3>另外一个labels</h3>
</script>
step2:定义一个factory,用来计算事件源的位置坐标(从一个网站“偷”来的,部分代码反混淆了)
angular.module("ui.bootstrap.position", []).factory("$position", ["$document", "$window",
function ($document, $window) {
function getStyle(element, styleName) {
return element.currentStyle ? element.currentStyle[styleName] : $window.getComputedStyle ? $window.getComputedStyle(element)[styleName] : element.style[styleName];
}
function isStatic(element) {
return "static" === (getStyle(element, "position") || "static")
}
var e = function (b) {
for (var c = $document[0], e = b.offsetParent || c; e && e !== c && isStatic(e) ;)
e = e.offsetParent;
return e || c
};
return {
position: function (element) {
var offset = this.offset(element),
parentOffset = {
top: 0,
left: 0
},
f = e(element[0]);
f != $document[0] && (parentOffset = this.offset(angular.element(f)), parentOffset.top += f.clientTop - f.scrollTop, parentOffset.left += f.clientLeft - f.scrollLeft);
var g = element[0].getBoundingClientRect();
return {
width: g.width || element.prop("offsetWidth"),
height: g.height || element.prop("offsetHeight"),
top: offset.top - parentOffset.top,
left: offset.left - parentOffset.left
}
},
offset: function (c) {
var d = c[0].getBoundingClientRect();
return {
width: d.width || c.prop("offsetWidth"),
height: d.height || c.prop("offsetHeight"),
top: d.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
left: d.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
}
},
positionElements: function (a, b, c, d) {
var e, f, g, h, i = c.split("-"),
j = i[0],
k = i[1] || "center";
e = d ? this.offset(a) : this.position(a),
f = b.prop("offsetWidth"),
g = b.prop("offsetHeight");
var l = {
center: function () {
return e.left + e.width / 2 - f / 2
},
left: function () {
return e.left
},
right: function () {
return e.left + e.width
}
},
m = {
center: function () {
return e.top + e.height / 2 - g / 2
},
top: function () {
return e.top
},
bottom: function () {
return e.top + e.height
}
};
switch (j) {
case "right":
h = {
top: m[k](),
left: l[j]()
};
break;
case "left":
h = {
top: m[k](),
left: e.left - f
};
break;
case "bottom":
h = {
top: m[j](),
left: l[k]()
};
break;
default:
h = {
top: e.top - g,
left: l[k]()
}
}
return h
}
}
}]);
step3:定义$popbox 使用provider,
angular.module("ui.bootstrap.popbox", []).provider("$popbox", function () {
this.$get = ["$document", "$templateCache", "$compile", "$rootScope", "$position",
function ( $document, $templateCache, $compile, $rootScope, $position) {
function popbox(options) {
var _this = this;
this.options = angular.extend({}, options);
var bodyEl = $document.find("body");
var dom = angular.element($templateCache.get("popbox.html"));
dom.append($templateCache.get(this.options.templateUrl));
this.elmentEL = dom;
this.handleBackDropClick = function ($event) {
for (var element = $event.target, notpopbox = true; element;) {
if ($(element).hasClass("popbox")) {
notpopbox = !1;
break
}
element = element.parentNode
}
if (notpopbox)
_this.close();
};
this.close = function () {
_this.elmentEL.remove();
};
this.open = function ($event, $scope) {
$(".popbox").each(function () {
$(this).scope().popbox.close()
})
var sourcePosition = $position.position($($event.target));
this.elmentEL.css("top", sourcePosition.top + sourcePosition.height + "px");
this.elmentEL.css("left", sourcePosition.left + "px");
this.elmentEL.css("width", _this.options.width + "px");
this.elmentEL.css("height", _this.options.height + "px");
bodyEl.append(this.elmentEL);
$compile(this.elmentEL)($scope);
$document.bind("mousedown", this.handleBackDropClick);
};
}
return {
popbox: function (options) {
return new popbox(options)
}
};
}];
});
step4:定义module依赖关系,初始化app对象
angular.module("ui.bootstrap", ["ui.bootstrap.position", "ui.bootstrap.popbox"]);
//定义了上面一行后,app初始化时只需要定义依赖ui.bootstrap就可以了。
var app = angular.module('app', ['ngRoute', 'ngResource', "chart.js", "ui.bootstrap"]);
step5:定义controll
app.controller("dashbordCtl", function ($scope, $http, $location, $popbox) {
$scope.js_openlabel = function ($event) {
$popbox.popbox({ templateUrl: "labels.html", width: 100, height: 300 }).open($event, $scope);
}
$scope.js_openlabel1 = function ($event) {
$popbox.popbox({ templateUrl: "labels1.html", width: 200, height: 400 }).open($event, $scope);
}
//这里定义了两个方法用来打开弹出框,注意两个方法中使用templateUrl是不一样的。
});
step6:html标签
<body ng-controller="dashbordCtl">
<h4>测试popbox</h4>
给按钮几个left
<button ng-click="js_openlabel($event)">popbox</button>
<br />
<br />
<br />
<button ng-click="js_openlabel1($event)">popbox</button>
</body>
ok下面可以看效果了。
版权声明:本文为aley原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。