AngularJS自定义Directive
(编辑完这篇之后,发现本篇内容应该属于AngularJS的进阶,内容有点多,有几个例子偷懒直接用了官方的Demo稍加了一些注释,敬请见谅)。
前面一篇介绍了各种常用的AngularJS内建的Directives以及对应的代码实例。这篇我们再看看如何创建自己的Directive吧!
什么时候需要自定义Directive?
1. 使你的Html更具语义化,不需要深入研究代码和逻辑即可知道页面的大致逻辑。
2. 抽象一个自定义组件,在其他地方进行重用。
看一下如下2个代码片段:
示例1:
<body>
<div>
<p>This is your class name.</p>
<div>
<p>Your teacher:</p>
<p>Mr. Wang</p>
<p>35 years old</p>
<p>English</p>
<p>Descriptions: 1.85cm tall, with a pair of brown glasses, unmarried, easy going etc.</p>
</div>
<div>
<div>
<p>Students in the class:</p>
<div>
<p>Jack</p>
<p>Male</p>
<p>15</p>
<p>Description: Smart ...</p>
</div>
<div>
<p>May</p>
<p>Female</p>
<p>14</p>
<p>Description: Diligent ...</p>
</div>
<div>
<p>Tom</p>
<p>Male</p>
<p>15</p>
<p>Description: Naughty ...</p>
</div>
<div>
<p>Alice</p>
<p>Female</p>
<p>14</p>
<p>Description: Smart ...</p>
</div>
</div>
</div>
</div>
</body>
示例2:
<body ng-app>
<class-info>
<teacher-info></teacher-info>
<student-infos></student-infos>
</class-info>
</body>
示例1中的代码你可能要完整的看完才能知道逻辑(当然示例1也不复杂,你可以想象下真实的场景要比这个复杂的多的多),不是说示例2中的代码少(逻辑被转移到其他地方去了),而是在示例2中,光看Html标签就知道这个页面是在展示班级信息,班级信息中还有班主任的信息和所有学生的信息。
另外,示例1中,若一个班级的学生有30个,学生信息的Html会出现30次,如果将来发生变动,这30出学生信息的代码都需要改动。
制作一个属于自己的Directive
示例3:
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('ngCustomDirectiveTest', []);
app.controller('myController', ['$scope', function ($scope) {
$scope.info = {
yourname: 'Jack',
template: 'template.html'
};
}]); // 自定义Element的Directive
app.directive("studentInfo", function () {
return {
// A 代表 Attribute
// C 代表 Class
// E 代表 Element
// ACE 表示同时创建 A、C、E 三种
restrict: 'ACE',
// templateUrl 指向独立的Html文件,AngularJS会用Html文件中的内容替换studentInfo对象
templateUrl: 'template.html'
};
});
})();
</script>
</head>
<body ng-app="ngCustomDirectiveTest">
<div ng-controller="myController as myCtrl">
<student-info></student-info>
<br />
<data-student-info></data-student-info>
<br /> <div student-info></div>
<br />
<div data_student-info></div>
<br /> <div class="student-info"></div>
<br />
<div class="data-student-info"></div>
<br />
</div>
</body>
</html>
template.html:
<div>
<p>This is a custom template.</p>
<p>Your name: {{info.yourname}}</p>
</div>
注意:你可能还见过restrict:'M',或者Directive的命名以pre_suf、pre:suf这样的代码书写方式,这些都已经“过时”了,最潮的restrict仅使用ACE三种,命名方式使用pre-suf。
另外,你可能疑惑,为什么加上"data-"前缀的为什么也能被解析?实际上AngularJS在处理Directive时,首先会忽略Directive命名中的"data-"或者"x-"前缀,因此无论你加上"data-"还是"x-",AngularJS还是能正确解析的,不过"x-"也是一种过时的写法,我们可以忽略。
好了,是不是很容易?属于我们自己的Directive就这样创建成功了,接着让我们更深入一些,看一下Directive的scope属性。首先看一下以下3段代码:
示例4(student-info直接使用了包含它的Controller的Scope中的变量jack和alice):
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('ngCustomDirectiveTest', []);
app.controller('myController', ['$scope', function ($scope) {
$scope.jack = {
name: 'Jack',
sex: 'Male'
},
$scope.alice = {
name: 'Alice',
sex: 'Female'
}
}]); app.directive("studentInfo", function () {
return {
restrict: 'E',
template: '<div><p>Student name: {{jack.name}}</p><p>Student sex: {{jack.sex}}</p></div><br /><div><p>Student name: {{alice.name}}</p><p>Student sex: {{alice.sex}}</p></div>'
};
});
})();
</script>
</head>
<body ng-app="ngCustomDirectiveTest">
<div ng-controller="myController as myCtrl">
<student-info></student-info>
</div>
</body>
</html>
示例5(和示例1类似,直接使用包含student-info的Controller中的变量students,在template中使用ng-repeat展示学生信息):
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('ngCustomDirectiveTest', []);
app.controller('myController', ['$scope', function ($scope) {
$scope.students = [
{
name: 'Jack',
sex: 'Male'
},
{
name: 'Alice',
sex: 'Female'
}
];
}]); app.directive("studentInfo", function () {
return {
restrict: 'E',
template: '<div ng-repeat="stu in students"><p>Student name:{{stu.name}}</p><p>Student sex:{{stu.sex}}</p></div>'
};
});
})();
</script>
</head>
<body ng-app="ngCustomDirectiveTest">
<div ng-controller="myController as myCtrl">
<student-info></student-info>
</div>
</body>
</html>
示例6(定义两个不同的Controller:jackController和aliceController,使student-info处于2个不同的controller中):
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('ngCustomDirectiveTest', []);
app.controller('jackController', ['$scope', function ($scope) {
$scope.student =
{
name: 'Jack',
sex: 'Male'
}
}]); app.controller('aliceController', ['$scope', function ($scope) {
$scope.student =
{
name: 'Alice',
sex: 'Female'
}
}]); app.directive("studentInfo", function () {
return {
restrict: 'E',
template: '<div><p>Student name:{{student.name}}</p><p>Student sex:{{student.sex}}</p></div>'
};
});
})();
</script>
</head>
<body ng-app="ngCustomDirectiveTest">
<div ng-controller="jackController as jackCtrl">
<student-info></student-info>
</div>
<br />
<div ng-controller="aliceController as aliceCtrl">
<student-info></student-info>
</div>
</body>
</html>
上述三种方式,都能达到我们所需的目的:自定义一个名为student-info的Directive,展示Controller中的学生信息。但仔细分析上述3种不同的代码,能发现它们各自有不同的问题:
1. 示例4中,student-info的template中的所有表达式严重依赖Controller中的变量定义,导致student-info无法抽象成一个公共的学生信息展示模块。
2. 示例5中,虽然使用ng-repeat封装了代码,但是还是存在依赖Controller中students变量的问题,示例5仅比示例4稍微好点。
3. 示例6中,定义了不同的Controller来隔离作用域,但N个学生需要定义N个作用域,并且定义Controller时,还是必须定义一个名为student的变量,否则代码无法正确执行,因此还是存在耦合性。
好吧,让我们看看AngularJS为我们提供的优雅的解决方案-Isolate scope:
示例7(通过使用=attr将Isolate scope中的属性赋值给Directive的名为'attr'的Attribute):
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('ngCustomDirectiveTest', []);
app.controller('myController', ['$scope', function ($scope) {
$scope.jack = {
name: 'Jack',
sex: 'Male'
},
$scope.alice = {
name: 'Alice',
sex: 'Female'
}
}]); app.directive("studentInfo", function () {
return {
restrict: 'E',
// 定义student-info的Isolate scope
scope: {
// 作用域内定义一个变量:newNameInScope
// 值对应到Directive中的info属性
newNameInScope: '=info'
},
// template 不再依赖外部, 仅依赖内部的newNameInScope变量
template: '<div><p>Student name: {{newNameInScope.name}}</p><p>Student sex: {{newNameInScope.sex}}</p></div>'
};
});
})();
</script>
</head>
<body ng-app="ngCustomDirectiveTest">
<div ng-controller="myController as myCtrl">
<!--将myController中的jack属性传递给info-->
<student-info info="jack"></student-info>
<br />
<!--将myController中的alice属性传递给info-->
<student-info info="alice"></student-info>
</div>
</body>
</html>
不同之处已经在注释中说明,示例7已经完全将student-info与外界隔离,不在存在耦合性,真正达到了我们自定义Directive的目的2(见本文"什么时候需要自定义Directive"部分)。
让我们再对示例7进行一些调整:
示例8:
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('ngCustomDirectiveTest', []);
app.controller('myController', ['$scope', function ($scope) {
$scope.jack = {
name: 'Jack',
sex: 'Male'
},
$scope.alice = {
name: 'Alice',
sex: 'Female'
}
}]); app.directive("studentInfo", function () {
return {
restrict: 'E',
scope: {
newNameInScope: '=info'
},
// 这里的alice将不能获取Controller中的变量alice的信息
template: '<div><p>Student name: {{newNameInScope.name}}</p><p>Student sex: {{newNameInScope.sex}}</p><br /><p>Deskmate name: {{alice.name}}</p><p>Deskmate sex: {{alice.sex}}</p></div>'
};
});
})();
</script>
</head>
<body ng-app="ngCustomDirectiveTest">
<div ng-controller="myController as myCtrl">
<student-info info="jack"></student-info>
</div>
</body>
</html>
这个就是所谓的封闭(Isolate),对比一下示例4,当创建student-info时指定了scope属性后,不在scope中指定的变量,在student-info中将无法被识别,做到了“封闭”。这样,当你定义一个公共模块时,不会因为在不同的Controller中使用而产生意想不到的问题。因此当你需要定义一个具有隔离性的Directive时,即使不需要传递Controller中的变量,也务必加上scope属性。
不过我们只能将一个字符串或者一个对象传入Isolate scope中,试想若遇到某些特殊情况,需要直接包含指定的Html片段时怎么办?AngularJS也是有这样的功能的。
示例9:
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('ngCustomDirectiveTest', []);
app.controller('myController', ['$scope', function ($scope) {
$scope.jack = {
name: 'Jack',
sex: 'Male'
},
$scope.alice = {
name: 'Alice',
sex: 'Female'
}
}]); app.directive("studentInfo", function () {
return {
restrict: 'E',
// 指定transclude属性为true
transclude: true
};
});
})();
</script>
</head>
<body ng-app="ngCustomDirectiveTest">
<div ng-controller="myController as myCtrl">
<!--指明student-info将会使用transclude模式-->
<student-info ng-transclude>
<!-- student-info的内容由使用者自己指定,并且内容中能访问student-info的scope以外的变量 -->
<p>Student name: {{jack.name}}</p>
<p>Student sex: {{jack.sex}}</p>
<br />
<p>Deskmate name: {{alice.name}}</p>
<p>Deskmate sex: {{alice.sex}}
</student-info>
</div>
</body>
</html>
其他自定义Directive的示例
示例10(自定义Directive操作DOM,官方文档中的demo):
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('docsTimeDirective', []); app.controller('Controller', ['$scope', function ($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
}]) app.directive('myCurrentTime', ['$interval', 'dateFilter', function ($interval, dateFilter) {
function link(scope, element, attrs) {
var format,
timeoutId; function updateTime() {
element.text(dateFilter(new Date(), format));
} scope.$watch(attrs.myCurrentTime, function (value) {
format = value;
updateTime();
}); element.on('$destroy', function () {
$interval.cancel(timeoutId);
}); timeoutId = $interval(function () {
updateTime();
}, 1000);
} return {
link: link
};
}]);
})();
</script>
</head>
<body ng-app="docsTimeDirective">
<div ng-controller="Controller">
Date format:
<input ng-model="format">
<hr />
Current time is: <span my-current-time="format"></span>
</div>
</body>
</html>
如果想要使Directive改变DOM,一般会用到link参数,其原型为:function link(scope, element, attrs) {...}:
- scope: 与当前元素结合的scope
- elment:当前元素
- $attrs:当前元素的属性对象
示例11(通过使用&attr开放Directive,将自定义的方法绑定到Directive上):
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('isoFnBindTest', []); app.controller('myController', ['$scope', function ($scope) {
$scope.name = '';
$scope.message = '';
$scope.isHide = true;
$scope.sayHello = function (message, name) {
$scope.isHide = false;
$scope.name = name;
$scope.message = message;
alert($scope.message + ',' + $scope.name);
};
}]); app.directive('myGreeting', function () {
return {
restrict: 'E',
transclude: true,
scope: {
// Step 2: greet方法绑定到onGreet属性(对应Html中的on-greet),并将greet的输入参数传给onGreet
'greet': '&onGreet'
},
templateUrl: 'my-greeting.html'
};
});
})();
</script>
</head>
<body ng-app="isoFnBindTest">
<div ng-controller="myController">
<!-- Step 3: on-greet指向了myController中的sayHello方法,此时on-greet中能直接访问到greet的输入参数-->
<my-greeting on-greet="sayHello(message, name)">
<div ng-hide="isHide">
{{message}}, {{name}}!
</div>
</my-greeting>
</div>
</body>
</html>
my-greeting.html:
<div>
<!-- Step1: 一旦触发click, 将调用Isolate scope中的greet方法-->
<button ng-click="greet({message: 'Hello', name: 'Tom'})">Click me!</button>
<div ng-transclude></div>
</div>
示例12(Directive侦听事件,官方Demo):
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('dragModule', []); app.directive('myDraggable', ['$document', function ($document) {
return {
link: function (scope, element, attr) {
var startX = 0, startY = 0, x = 0, y = 0; element.css({
position: 'relative',
border: '1px solid red',
backgroundColor: 'lightgrey',
cursor: 'pointer'
}); element.on('mousedown', function (event) {
// Prevent default dragging of selected content
event.preventDefault();
startX = event.pageX - x;
startY = event.pageY - y;
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
}); function mousemove(event) {
y = event.pageY - startY;
x = event.pageX - startX;
element.css({
top: y + 'px',
left: x + 'px'
});
} function mouseup() {
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
}
}
};
}]);
})();
</script>
</head>
<body ng-app="dragModule">
<span my-draggable>Drag ME</span>
</body>
</html>
示例13(Directive之间的相互作用,官方Demo):
<!DOCTYPE>
<html>
<head>
<script src="/Scripts/angular.js"></script>
<script type="text/javascript">
(function () {
var app = angular.module('docsTabsExample', []); app.directive('myTabs', function () {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function ($scope) {
var panes = $scope.panes = []; $scope.select = function (pane) {
angular.forEach(panes, function (pane) {
pane.selected = false;
});
pane.selected = true;
}; this.addPane = function (pane) {
if (panes.length === 0) {
$scope.select(pane);
}
panes.push(pane);
};
},
templateUrl: 'my-tabs.html'
};
}); app.directive('myPane', function () {
return {
// 指定必须有myTabs对象,若对象不存在则会报错,见下面的图1
require: '^myTabs', // ^ 表示将在父级的范围内查找该对象, 没有 ^ 表示在Directive内查找该对象, 若范围指定错误无法找到myTabs,js则会报错
restrict: 'E',
transclude: true,
scope: {
title: '@'
},
link: function (scope, element, attrs, tabsCtrl) {
tabsCtrl.addPane(scope);
},
templateUrl: 'my-pane.html'
};
});
})();
</script>
</head>
<body ng-app="docsTabsExample">
<my-tabs>
<my-pane title="Hello">
<h4>Hello</h4>
<p>Lorem ipsum dolor sit amet</p>
</my-pane>
<my-pane title="World">
<h4>World</h4>
<em>Mauris elementum elementum enim at suscipit.</em>
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
</my-pane>
</my-tabs>
</body>
</html>
my-tabs.html:
<div class="tabbable">
<ul class="nav nav-tabs">
<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">
<a href="" ng-click="select(pane)">{{pane.title}}</a>
</li>
</ul>
<div class="tab-content" ng-transclude></div>
</div>
my-pane.html:
<div class="tab-pane" ng-show="selected" ng-transclude>
</div>
参考资料
AngularJS官方文档:https://docs.angularjs.org/guide/directive
CodeSchool快速入门视频:http://campus.codeschool.com/courses/shaping-up-with-angular-js/intro
AngularJS自定义Directive的更多相关文章
- AngularJS自定义Directive中link和controller的区别
在AngularJS中,自定义Directive过程中,有时用link和controller都能实现相同的功能.那么,两者有什么区别呢? 使用link函数的Directive 页面大致是: <b ...
- AngularJS自定义Directive不一定返回对象
AngularJS中,当需要自定义Directive时,通常返回一个对象,就像如下的写法: angular.module('modulename') .directive('myDirective', ...
- AngularJS自定义Directive初体验
通常我们这样定义个module并随之定义一个controller. var app = angular.module('myApp', []); app.controller('CustomersCo ...
- AngularJS自定义Directive与controller的交互
有时候,自定义的Directive中需要调用controller中的方法,即Directive与controller有一定的耦合度. 比如有如下的一个controller: app.controlle ...
- 理解AngularJS生命周期:利用ng-repeat动态解析自定义directive
ng-repeat是AngularJS中一个非常重要和有意思的directive,常见的用法之一是将某种自定义directive和ng-repeat一起使用,循环地来渲染开发者所需要的组件.比如现在有 ...
- AngularJs中,如何在父元素中调用子元素为自定义Directive中定义的函数?
最近一段时间准备使用AngularJs中的自定义Directive重构一下代码. 在这里说明一下,把自定义控件封装成Directive并不一定是要复用,而是要让代码结构更加清晰.就好像你将一个长方法拆 ...
- 前端angularJS利用directive实现移动端自定义软键盘的方法
最近公司项目的需求上要求我们iPad项目上一些需要输入数字的地方用我们自定义的软键盘而不是移动端设备自带的键盘,刚接到需求有点懵,因为之前没有做过,后来理了一下思路发现这东西也就那样.先看一下实现之后 ...
- angularjs自定义指令Directive
今天学习angularjs自定义指令Directive.Directive是一个非常棒的功能.可以实现我们自义的的功能方法. 下面的例子是演示用户在文本框输入的帐号是否为管理员的帐号"Adm ...
- AngularJs(Part 11)--自定义Directive
先对自定义Directive有一个大体的映像 myModule.directive('myDirective',function(injectables){ var directiveDefiniti ...
随机推荐
- android触控,先了解MotionEvent(一)
http://my.oschina.net/banxi/blog/56421 这是我个人的看法,要学好android触控,了解MotionEvent是必要,对所用的MotionEvent常用的API要 ...
- 2018.09.26 bzoj1015: [JSOI2008]星球大战starwar(并查集)
传送门 并查集经典题目. 传统题都是把删边变成倒着加边,这道题是需要倒着加点. 处理方法是将每个点与其他点的边用一个vector存起来,加点时用并查集统计答案就行了. 代码: #include< ...
- 着重基础之—Java 8 Comparator: How to Sort a List (List排序)
着重基础之—Java 8 Comparator: How to Sort a List (List排序) 首先申明,这篇博客的内容不是我自己的知识,我是从国外网站搬来的,原因有二:1是因为大天朝对网络 ...
- 微信小程序底部导航Tabbar
1,底部导航栏这个功能是非常常见的一个功能,基本上一个完成的app,都会存在一个导航栏,那么微信小程序的导航栏该怎么实现呢?经过无数的踩坑,终于实现了,好了,先看看效果图. 2,对于底部导航栏,小程序 ...
- Before an Exam
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=93241#problem/B (654123) http://codeforces.com ...
- (欧拉公式 很水) Coprimes -- sgu -- 1002
链接: http://vj.acmclub.cn/contest/view.action?cid=168#problem/B Coprimes 时限:250MS 内存:4096KB 6 ...
- HDU6029 Graph Theory 2017-05-07 19:04 40人阅读 评论(0) 收藏
Graph Theory Time Limit: 2000/1000 M ...
- 阿里Sophix热修复
阿里巴巴对Android热修复技术已经进行了长达多年的探索. 最开始,是手淘基于Xposed进行了改进,产生了针对Android Dalvik虚拟机运行时的Java Method Hook技术,Dex ...
- Android-自定义ListView下拉刷新与上拉加载
效果图: 第一步:编写需要在ListView中增加头加载的布局文件,与底部加载的布局文件: 头布局文件: <?xml version="1.0" encoding=" ...
- 权限管理系统系列之WCF通信
目录 权限管理系统系列之序言 首先说下题外话,有些园友看了前一篇[权限管理系统系列之序言]博客加了QQ群(186841119),看了我写的权限管理系统的相关文档(主要是介绍已经开发的功能),给出了一 ...