除了 AngularJS 内置的63个指令外,我们还可以创建自定义指令。你可以使用 .directive 函数来添加自定义的指令。要调用自定义指令,HTML 元素上需要添加自定义指令名。使用驼峰法来命名一个指令, runoobDirective, 但在使用它时需要以 - 分割, runoob-directive,自定义指令的参数如下:
-
angular.module('app', []).directive('myDirective', function() {
-
return {
-
restrict:String,
-
priority:Number,
-
terminal:Boolean,
-
template:String or Template Function,
-
templateUrl:String or Template Function,
-
replace:Boolean or String,
-
transclude:Boolean,
-
scope:Boolean or Object,
-
controller:String or function(scope, element, attrs, transclude, otherInjectables) { ... },
-
controllerAs:String,
-
require:String,
-
link: function(scope, iElement, iAttrs) { ... },
-
compile:function(tElement, tAttrs, transclude) {
-
return {
-
pre: function(scope, iElement, iAttrs, controller) { ... },
-
post: function(scope, iElement, iAttrs, controller) { ... }
-
}
-
return function postLink(...) { ... }
-
}
-
};
-
});
1、restrict:String
restrict是申明标识符在模板中作为元素,属性,类,注释或组合,如何使用。有四个值:E、A、C、M

注意:
(1)、推荐使用元素和属性的方式使用指令
(2)、当需要创建带有自己的模板的指令时,使用元素名称的方式创建指令
(3)、当需要为已有的HTML标签增加功能时,使用属性的方式创建指令
实例:
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
</head>
-
<body>
-
<hello></hello>
-
<div hello></div>
-
<div class="hello"></div>
-
<!-- directive:hello -->
-
</body>
-
<script src="framework/angular-1.3.0.14/angular.js"></script>
-
<script>
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("hello", function() {
-
return {
-
restrict: 'AEMC',
-
template: '<div>Hi everyone!</div>',
-
replace: true
-
}
-
});
-
</script>
-
</html>

2、priority: Number
priority是指令执行优先级。若在单个DOM上有多个指令,则优先级高的先执行;
设置指令的优先级算是不常用的
3、template: String or Function
(1)、字符串
template是指令链接DOM模板
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
</head>
-
<body>
-
<hello></hello>
-
</body>
-
<script src="framework/angular-1.3.0.14/angular.js"></script>
-
<script>
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("hello", function() {
-
return {
-
restrict: 'E',
-
template: '<div>hello world</div>',
-
replace: true
-
}
-
});
-
</script>
-
</html>
(2)、函数
Function一个函数,可接受两个参数tElement和tAttrs。其中tElement是指使用此指令的元素,而tAttrs则实例的属性,它是一个由元素上所有的属性组成的集合(对象)形如:
-
{
-
name:‘shanshuizinong,
-
age:23
-
}
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
</head>
-
<body>
-
<hello name='shanshuizinong'></hello>
-
</body>
-
<script src="js/angular.min.js"></script>
-
<script>
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("hello", function() {
-
return {
-
restrict: 'E',
-
replace: true,
-
template: function(tElement,tAttrs){
-
var html = '';
-
html += '<div>'+tAttrs.name+'</div>';
-
return html;
-
}
-
}
-
});
-
</script>
-
</html>

4、emplateUrl: String
templateUrl是指定一个字符串式的内嵌模板,如果你指定了模板是一个URL,那么是不会使用的。
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
<link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
-
</head>
-
<body>
-
<hello></hello>
-
</body>
-
<script src="framework/angular-1.3.0.14/angular.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("hello", function() {
-
return {
-
restrict: 'AECM',
-
templateUrl: 'hello.html',
-
replace: true
-
}
-
});
-
</script>
-
</html>

由于加载html模板是通过异步加载的,若加载大量的模板会拖慢网站的速度,这里有个技巧,就是先缓存模板。
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
</head>
-
<body>
-
<hello></hello>
-
</body>
-
<script src="framework/angular-1.3.0.14/angular.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
//注射器加载完所有模块时,此方法执行一次;缓存
-
myModule.run(function($templateCache){
-
$templateCache.put("hello.html","<div>Hello world!!!</div>");
-
});
-
myModule.directive("hello", function($templateCache) {
-
return {
-
restrict: 'AECM',
-
template: $templateCache.get("hello.html"),
-
replace: true
-
}
-
});
-
</script>
-
</html>

5、replace: Boolean
replace是指令链接模板是否替换原有元素
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
</head>
-
<body>
-
<hello>
-
<div>这里是指令内部的内容。</div>
-
</hello>
-
</body>
-
<script src="js/angular.min.js" type="text/javascript" charset="utf-8"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("hello", function() {
-
return {
-
restrict:"AE",
-
template:"<div>Hello World!</div>",
-
replace:true
-
}
-
});
-
</script>
-
</html>

6、transclude: Boolean
transclude是移动一个标识符的原始字节带到一个新模块的位置。当你开启transclude后,你就可以使用ng-transclude来指明了应该在什么地方放置transcluded内容。
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
</head>
-
<body>
-
<hello>
-
<div>这里是指令内部的内容。</div>
-
</hello>
-
</body>
-
<script src="js/angular.min.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("hello", function() {
-
return {
-
restrict:"AE",
-
transclude:true,
-
template:"<div>Hello everyone!<div ng-transclude>你看不见我</div></div>"
-
}
-
});
-
</script>
-
</html>

7、link:Function
link通过代码修改目标DOM元素的实例,添加事件监听,建立数据绑定。compile函数用来对模板自身进行转换,而link函数负责在模型和视图之间进行动态关联;compile函数仅仅在编译阶段运行一次,而对于指令的每个实例,link函数都会执行一次;compile可以返回preLink和postLink函数,而link函数只会返回postLink函数,如果需要修改DOM结构,应该在postLink中来做这件事情,而如果在preLink中做这件事情会导致错误;大多数时候我们只要编写link函数即可。
link函数有四个参数分别为:
(1)scope,与指令元素相关联的作用域
(2)element,当前指令对应的 元素
(3)attrs,由当前元素的属性组成的对象
(4)supermanCtrl,若指令中定义有require选项,则会有supermanCtrl参数,代表控制器或者所依赖的指令的控制器。
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
</head>
-
<body>
-
<hello>Hello World!</hello>
-
</body>
-
<script src="js/angular.min.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("hello", function() {
-
return {
-
restrict:"AE",
-
link:function(scope,element,attrs,supermanCtrl){
-
console.log(element);
-
element.bind("mouseenter",function(){
-
console.log("鼠标进入...");
-
});
-
element.bind("mouseout",function(){
-
console.log("鼠标滑出...");
-
});
-
}
-
}
-
});
-
</script>
-
</html>

下面实例通过link函数实现指令的复用,两个控制器与一个指令之间进行交互。指令可以调用控制器MyCtrl和MyCtrl2中的方法,但是需要注意我们必须使用属性,因为两个控制器的方法不同。
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
</head>
-
<body>
-
<div ng-controller="MyCtrl">
-
<loader howToLoad="loadData()">滑动加载</loader>
-
</div>
-
<div ng-controller="MyCtrl2">
-
<loader howToLoad="loadData2()">滑动加载</loader>
-
</div>
-
</body>
-
<script src="framework/angular-1.3.0.14/angular.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.controller('MyCtrl', ['$scope', function($scope){
-
$scope.loadData=function(){
-
console.log("加载数据中111...");
-
}
-
}]);
-
myModule.controller('MyCtrl2', ['$scope', function($scope){
-
$scope.loadData2=function(){
-
console.log("加载数据中222...");
-
}
-
}]);
-
myModule.directive("loader", function() {
-
return {
-
restrict:"AE",
-
link:function(scope,element,attrs){
-
element.bind('mouseenter', function(event) {
-
//注意这里的坑,howToLoad会被转换成小写的howtoload
-
scope.$apply(attrs.howtoload);
-
});
-
}
-
}
-
});
-
</script>
-
</html>

8、controller和require
(1)、controller
controller创建一个控制器通过标识符公开通信API。给指令暴露出一组public方法,给外部调用的。
1)、字符串
若是为字符串,则将字符串当做是控制器的名字,来查找注册在应用中的控制器的构造函数
-
angular.module('myApp', []) .directive('myDirective', function() {
-
restrict: 'A',
-
controller: 'SomeController'
-
})
-
angular.module('myApp').controller('SomeController', function($scope, $element, $attrs, $transclude) {
-
// 控制器逻辑放在这里
-
});
2)、匿名函数
也可以直接在指令内部的定义为匿名函数,同样我们可以再这里注入任何服务($log,$timeout等等)
-
angular.module('myApp',[]) .directive('myDirective', function() {
-
restrict: 'A',
-
controller: function($scope, $element, $attrs, $transclude) {
-
// 控制器逻辑放在这里
-
}
-
});
另外还有一些特殊的服务(参数)可以注入
(a)$scope,与指令元素相关联的作用域
(b)$element,当前指令对应的元素
(c)$attrs,由当前元素的属性组成的对象
(d)$transclude,嵌入链接函数,实际被执行用来克隆元素和操作DOM的函数
注意: 除非是用来定义一些可复用的行为,一般不推荐在这使用。
指令的控制器和link函数可以进行互换。区别在于,控制器主要是用来提供可在指令间复用的行为但link链接函数只能在当前内部指令中定义行为,且无法再指令间复用。
下面是实例js中包含controller的一部分:定义了三个公共方法,供外部指令访问。
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("superman", function() {
-
return {
-
scope: {},
-
restrict: 'AE',
-
controller: function($scope) {
-
$scope.abilities = [];
-
this.addStrength = function() {
-
$scope.abilities.push("strength");
-
};
-
this.addSpeed = function() {
-
$scope.abilities.push("speed");
-
};
-
this.addLight = function() {
-
$scope.abilities.push("light");
-
};
-
},
-
link: function(scope, element, attrs) {
-
element.addClass('btn btn-primary');
-
element.bind("mouseenter", function() {
-
console.log(scope.abilities);
-
});
-
}
-
}
-
});
(2)、require
require当前标识符需要另一个标识符提供正确的函数功能。require的值可以是字符串或者数组。字符串代表另一个指令的名字,它将会作为link函数的第四个参数。controller的用法分为两种情形,一种是require自定义的controller,由于自定义controller中的属性方法都由自己编写,使用起来比较简单;另一种方法则是require AngularJS内建的指令,其中大部分时间需要require的都是ngModel这个指令。
下面是实例程序:假设现在我们要编写三个指令,三个指令中的link链接函数中存在有很多重合的方法,这时候我们就可以将这些重复的方法写在一个指令的controller中。然后在这三个指令中,require这个拥有controller字段的的指令,最后通过link链接函数的第四个参数就可以引用这些重合的方法了。
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
<link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
-
<script src="js/angular.min.js"></script>
-
</head>
-
<body>
-
<div class="row">
-
<div class="col-md-3">
-
<superman strength>动感超人---力量</superman>
-
</div>
-
</div><br>
-
<div class="row">
-
<div class="col-md-3">
-
<superman strength speed>动感超人2---力量+敏捷</superman>
-
</div>
-
</div><br>
-
<div class="row">
-
<div class="col-md-3">
-
<superman strength speed light>动感超人3---力量+敏捷+发光</superman>
-
</div>
-
</div>
-
</body>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.directive("superman", function() {
-
return {
-
scope: {},
-
restrict: 'AE',
-
controller: function($scope) {
-
$scope.abilities = [];
-
this.addStrength = function() {
-
$scope.abilities.push("strength");
-
};
-
this.addSpeed = function() {
-
$scope.abilities.push("speed");
-
};
-
this.addLight = function() {
-
$scope.abilities.push("light");
-
};
-
},
-
link: function(scope, element, attrs) {
-
element.addClass('btn btn-primary');
-
element.bind("mouseenter", function() {
-
console.log(scope.abilities);
-
});
-
}
-
}
-
});
-
myModule.directive("strength", function() {
-
return {
-
require: '^superman',
-
link: function(scope, element, attrs, supermanCtrl) {
-
supermanCtrl.addStrength();
-
}
-
}
-
});
-
myModule.directive("speed", function() {
-
return {
-
require: '^superman',
-
link: function(scope, element, attrs, supermanCtrl) {
-
supermanCtrl.addSpeed();
-
}
-
}
-
});
-
myModule.directive("light", function() {
-
return {
-
require: '^superman',
-
link: function(scope, element, attrs, supermanCtrl) {
-
supermanCtrl.addLight();
-
}
-
}
-
});
-
</script>
-
</html>

另外我们可以在require的参数值加上下面的某个前缀,这会改变查找控制器的行为:
(1)没有前缀,指令会在自身提供的控制器中进行查找,如果找不到任何控制器,则会抛出一个error
(2)?如果在当前的指令没有找到所需的控制器,则会将null传给link连接函数的第四个参数
(3)^如果在当前的指令没有找到所需的控制器,则会查找父元素的控制器
(4)?^组合
引用内置指令
-
angular.module('myApp')
-
.directive('spoint', function() {
-
return {
-
require: 'ngModel',
-
link: function(scope, elm, attrs, ctrl) {
-
ctrl.$parsers.unshift(function(viewValue) {
-
if (fibonacci.indexOf(parseInt(viewValue)) >= 0) {
-
ctrl.$setValidity('fibonacci', true);
-
return viewValue;
-
} else {
-
ctrl.$setValidity('fibonacci', false);
-
return undefined;
-
}
-
});
-
}
-
};
-
});
这里值得注意的是directive里link方法的第四个参数,我们在require里定义了ngModel 所以这里它是一个NgModelController,NgModelController是用来为ng-model提供了一组API。
9、scope
scope为这个标识符创建一个新的作用域,而不是继承符作用域。
(1)、scope的值分别为:false,true,{}
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
<link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
-
</head>
-
<body>
-
<div ng-controller='MyCtrl' style="margin-left: 10px;border: solid 2px greenyellow;padding: 10px;width:400px;">
-
Ctrl:<br />
-
<input ng-model="userName" />{{userName}}<br />
-
Directive:<br />
-
<hello></hello>
-
<hello></hello>
-
</div>
-
</body>
-
<script src="js/angular.min.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.controller("MyCtrl",function($scope){
-
$scope.userName="山水子农";
-
})
-
myModule.directive("hello", function() {
-
return {
-
restrict: 'AE',
-
scope:false,//ture or {}
-
template: '<div><input type="text" ng-model="userName"/>{{userName}}</div>',
-
replace: true
-
}
-
});
-
</script>
-
</html>
当scope为false时:Directive继承Ctrl的值,改变Ctrl的值,Directive的值也随之变化,反之亦如此。(继承不隔离)

当scope为true时:Directive继承Ctrl的值,改变Ctrl的值,Directive的值随之变化,但是改变Directive的值,Ctrl的值不变,兄弟Directive的值也不会变。(继承隔离)

当scope的值为{}时:没有继承Ctrl的值,所以Directive的值为空,改变任何一方的值均不能影响另一方的值。(不继承隔离)

(2)、scope的绑定策略
方法1:使用@(@attr)来进行单向文本(字符串)绑定
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
<link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
-
</head>
-
<body>
-
<div ng-controller="MyCtrl">
-
<drink flavor="{{ctrlFlavor}}"></drink>
-
</div>
-
</body>
-
<script src="js/angular.min.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.controller('MyCtrl', ['$scope', function($scope){
-
$scope.ctrlFlavor="AngularJs";
-
}])
-
myModule.directive("drink", function() {
-
return {
-
restrict:'AE',
-
template:"<div>{{flavor}}</div>",
-
link:function(scope,element,attrs){
-
scope.flavor=attrs.flavor;
-
}
-
}
-
});
-
</script>
-
</html>
上面的实例可以使用scope为flavor:'@'来代替link函数,注意@传递的是字符串不是对象。
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
<link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
-
</head>
-
<body>
-
<div ng-controller="MyCtrl">
-
<drink flavor="{{ctrlFlavor}}"></drink>
-
</div>
-
</body>
-
<script src="js/angular.min.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.controller('MyCtrl', ['$scope', function($scope){
-
$scope.ctrlFlavor="AngularJs";
-
}])
-
myModule.directive("drink", function() {
-
return {
-
restrict:'AE',
-
scope:{
-
flavor:'@'
-
},
-
template:"<div>{{flavor}}</div>"
-
}
-
});
-
</script>
-
</html>
方法二:使用=(=attr)进行双向绑定
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
<link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
-
</head>
-
<body>
-
<div ng-controller="MyCtrl" style="margin-left: 10px;border: solid 2px greenyellow;padding: 10px;width:200px;">
-
Ctrl:
-
<br>
-
<input type="text" ng-model="ctrlFlavor">
-
<br>
-
Directive:
-
<br>
-
<drink flavor="ctrlFlavor"></drink>
-
</div>
-
</body>
-
<script src="js/angular.min.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.controller('MyCtrl', ['$scope', function($scope){
-
$scope.ctrlFlavor="AngularJs";
-
}])
-
myModule.directive("drink", function() {
-
return {
-
restrict:'AE',
-
scope:{
-
flavor:'='
-
},
-
template:'<input type="text" ng-model="flavor"/>'
-
}
-
});
-
</script>
-
</html>

方法三:使用&来调用父作用域中的函数
可以利用前面的link函数取属性的方式来进行绑定,这里更方便的绑定方式就是利用&。
-
<!doctype html>
-
<html ng-app="MyModule">
-
<head>
-
<meta charset="utf-8">
-
<link rel="stylesheet" href="css/bootstrap-3.0.0/css/bootstrap.css">
-
</head>
-
<body>
-
<div ng-controller="MyCtrl">
-
<greeting greet="sayHello(name)"></greeting><br/>
-
<greeting greet="sayHello(name)"></greeting><br/>
-
<greeting greet="sayHello(name)"></greeting><br/>
-
</div>
-
</body>
-
<script src="js/angular.min.js"></script>
-
<script type="text/javascript">
-
var myModule = angular.module("MyModule", []);
-
myModule.controller('MyCtrl', ['$scope', function($scope){
-
$scope.sayHello=function(name){
-
alert("Hello "+name);
-
}
-
}])
-
myModule.directive("greeting", function() {
-
return {
-
restrict:'AE',
-
scope:{
-
greet:'&'
-
},
-
template:'<input type="text" ng-model="userName" /><br/>'+
-
'<button class="btn btn-default" ng-click="greet({name:userName})">Greeting</button><br/>'
-
}
-
});
-
</script>
-
</html>

