AngularJS Directive 自定义指令(我最喜欢AngularJs的功能之一)javascript
一:何时咱们会用到directivecss
1.使html更具语义化,不用深刻了解研究代码的逻辑即可知道大体逻辑。html
2.抽象出一个自定义组件,能够重复使用。java
二:directive的定义及其使用方法angularjs
1.下面是一个directive参数详细模板express
angular.module('app',[]);//申明一个调用angularjs块 angular.module('app').directive('directiveName', function factory() { var directiveDefinitionObject = { priority: 0,
restrict:'A', template: '<div></div>', templateUrl: 'directive.html', replace: false, transclude: false, scope: false, compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } }, link: function postLink(scope, iElement, iAttrs) { ... } }; return directiveDefinitionObject; });
2.参数详解数组
priority:缓存
(数字),可选参数,指明指令的优先级,若在单个DOM上有多个指令,则优先级高的先执行;安全
terminal:app
(布尔型),可选参数,值为true或false,若设置为true,则优先级低于此指令的其余指令则无效,不会被调用(优先级相同的仍是会执行);
restrict:
(字符串)可选参数,指明指令在DOM里面以什么形式被声明;
取值有:E(元素),A(属性),C(类),M(注释),其中默认值为A;固然也能够两个一块儿用,好比EA.表示便可以是元素也能够是属性。
E(元素):<directiveName></directiveName>
A(属性):<div directiveName='expression'></div>
C(类): <div class='directiveName'></div>
M(注释):<--directive:directiveName expression-->
通常状况下E/A/C用得比较多。
template:
(字符串或函数),可选参数
例:(1)字符串时:
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <hello-world></hello-world> </body> <script type="text/javascript"> angular.module('app', []); angular.module.('app').directive('helloWorld', function() { return { restrict: 'E', template: '<div><h1>Hi I am hello world.</h1></div>', replace: true }; }); </script> </html>
(2)函数时:有两个参数tElement和tAttrs
tElement:使用此指令的元素
tAttrs:该指令元素上的属性
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <hello-world></hello-world> <hello-world2 title = 'I am a Hello World 2.'></hello-world2> </body> <script type="text/javascript"> angular.module('app', []); app.directive('helloWorld', function() { return { restrict: 'E', template: '<div><h1>Hi I am a Hello Worl.</h1></div>', replace: true }; }); app.directive("helloWorld2",function(){ return{ restrict:'EAC', template: function(tElement,tAttrs){ var _html = ''; _html += '<div>'+tAttrs.title+'</div>'; return _html; } }; }); </script> </html>
templateUrl:
(字符串或者函数)可选参数
字符串:表明HTML文件路径的字符串
函数:可接收两个参数tElement和tAttrs(大体同上)
(PS:因为加载html模板是经过异步加载的,若加载大量的模板会拖慢网站的速度,因此咱们能够先缓存模板)
例:(把要加载的页面 先加载好 包含在你须要的html里)
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <hello-world></hello-world> </body> <script type="text/javascript"> angular.module('app', []); angular.module("app").directive('helloWorld', function() { return { restrict: 'E', templateUrl: 'hello.html', replace: true }; }); </script> <script type='text/ng-template' id='hello.html'> <div><h1>Hi I am Hello world.</h1></div> </script> </html>
还有一种方法:
angular.module("app").run(["$templateCache", function($templateCache) { $templateCache.put("hello.html", "<div><h1>Hi I am Hello world.</h1></div>"); }]);
replace:
(布尔值):默认值是false. true:替换掉指令即(<hello-world></hello-world>),false:不替换
transclude:(是否想要指令内部的内容被模板替换掉)
(布尔值):默认值是false. true:须要和ng transclude一块儿使用
例:template:"<div>hello every <div ng-transclude></div></div>"
这时,指令内部的内容会嵌入到ng-transclude这个div中。也就是变成了<div>hello every <div>这是指令内部的内容</div></div>。
scope:
1)默认值false。表示继承父做用域;(继承不隔离)
2)true。表示继承父做用域,并建立本身的做用域(子做用域);(继承隔离)
3){}。表示建立一个全新的隔离做用域;(不继承隔离)
(PS:当你想要建立一个可重用的组件时隔离做用域是一个很好的选择,经过隔离做用域咱们确保指令是‘独立'的,并能够轻松地插入到任何HTML app中,而且这种作法防止了父做用域被污染;)
下面针对隔离做用域进行详细讲解:
a.隔离做用域怎样去访问父做用域:
Directive 在使用隔离 scope 的时候,提供了三种方法同隔离以外的地方交互.
1). @
(用来访问 directive 外部环境定义的字符串值,主要是经过 directive 所在的标签属性绑定外部字符串值。这种绑定是单向的,即父 scope 的绑定变化,directive 中的 scope 的属性会同步变化, 而隔离 scope 中的绑定变化,父 scope 是不知道的。)PS:至关于继承隔离
scope:{
name:"@"
}
2). &
(& 方式提供一种途经是 directive 能在父 scope 的上下文中执行一个表达式。当 directive 中有什么动做须要更新到父 scope 中的时候,能够在父 scope 上下文中执行一段代码或者一个函数。PS:即绑定的是方法)
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <div ng-controller="myController"> <div>父scope: <div>Say:{{value}}</div> </div> <div>隔离scope: <div isolated-directive action="click()"></div> </div> </div> </body> <script type="text/javascript"> angular.module('app', []); app.controller("myController", function ($scope) { $scope.value = "hello world"; $scope.click = function () { $scope.value = Math.random(); }; }).directive("isolatedDirective", function () { return { scope: { action: "&" }, template: '<input type="button" value="在directive中执行父scope定义的方法" ng-click="action()"/>' } }) </script> </html>
3). =
(经过 directive 的 attr 属性的值在局部 scope 的属性和父 scope 属性名之间创建双向绑定。
意思是,当你想要一个双向绑定的属性的时候,你可使用=来引入外部属性。不管是改变父 scope 仍是隔离 scope 里的属性,父 scope 和隔离 scope 都会同时更新属性值,由于它们是双向绑定的关系。)
例:
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS Directive</title> <script type="text/javascript" src="angular.min.js"></script> </head> <body> <div ng-controller="myController"> <div>父scope: <div>Say:{{user.name}}<br>改变父scope的name:<input type="text" value="" ng-model="userBase.name"/></div> </div> <div>隔离scope: <div isolated-directive user="userBase"></div> </div> </div> </body> <script type="text/javascript"> angular.module('app', []); angular.module.("app").controller("myController", function ($scope) { $scope.userBase = { name: 'hello', id: 1 }; }).directive("isolatedDirective", function () { return { scope: { user: "=" }, template: 'Say:{{user.name}} <br>改变隔离scope的name:<input type="buttom" value="" ng-model="user.name"/>' } }) </script> </html>
controller:
(能够是一个字符串或者函数)
angular.module('app', []) angular.module.("app").directive('myDirective', function() { restrict: 'A', controller: 'DefinitionController' }) // 应用中其余的地方,能够是同一个文件或被index.html包含的另外一个文件 angular.module('app') .controller('DefinitionController', function($scope, $element, $a 也能够直接在指令内部的定义为匿名函数,一样咱们能够再这里注入任何服务($log,$timeout等等) js: angular.module('app',[]) .directive('myDirective', function() { restrict: 'A', controller: function($scope, $element, $attrs, $transclude) { // 控制器逻辑放在这里 } });
另外还有一些特殊的服务(参数)能够注入
(1)$scope,与指令元素相关联的做用域
(2)$element,当前指令对应的 元素
(3)$attrs,由当前元素的属性组成的对象
(4)$transclude,嵌入连接函数,实际被执行用来克隆元素和操做DOM的函数
注意: 除非是用来定义一些可复用的行为,通常不推荐在这使用。
指令的控制器和link函数(后面会讲)能够进行互换。区别在于,控制器主要是用来提供可在指令间复用的行为但link连接函数只能在当前内部指令中定义行为,且没法再指令间复用。
<!DOCTYPE html> <html lang="zh" ng-app="app"> <head> <meta charset="UTF-8"> <title>AngularJS入门学习</title> <script type="text/javascript" src="angular.min.js"></script> </head> <hello mycolor ="red">I am Hello World.</hello> </body> <script type="text/javascript"> angular.module('app', []); angular.module("app").directive('hello', function() { return { restrict: 'EA', transclude: true, //注意此处必须设置为true controller: function ($scope, $element,$attrs,$transclude,$log) { //在这里你能够注入你想注入的服务 $transclude(function (clone) { var a = angular.element('<p>'); a.css('color', $attrs.mycolor); a.text(clone.text()); $element.append(a); }); $log.info("hello everyone"); } }; }); </script> </html>
这里若是咱们想要实用的父做用域:$scope.$parent
新的做用域:$scope.$parent.new()
controllerAs
设置控制器的别名(angular1.2给咱们带来的新的语法糖)
例:
<script> angular.module('app',[]).directive('myDirective', function () { return { restrict: 'EA', transclude: true, controller:'someController', controllerAs:'mainController' //..其余配置 }; }); </script>
require
字符串或数组
字符串表明另外一个指令的名字,做为link函数的第四个参数。假设如今咱们要编写两个指令,两个指令中的link连接函数中存在有不少重合的方法,这时候咱们就能够将这些重复的方法写在第三个指令的controller中(上面也讲到controller常常用来提供指令间的复用行为)而后在这两个指令中,require这个拥有controller字段的的指令(第三个指令),最后经过link连接函数的第四个参数就能够引用这些重合的方法了。
例
<!doctype html> <html ng-app="app"> <head> <script src="angular.min.js"></script> </head> <body> <outer-directive> <inner-directive></inner-directive> <inner-directive2></inner-directive2> </outer-directive> <script> angular.module('app', []); angular.module("app").directive('outerDirective', function() { return { scope: {}, restrict: 'AE', controller: function($scope) { this.say = function(someDirective) { console.log('Show:' + someDirective.message); }; } }; }); angular.module("app").directive('innerDirective', function() { return { scope: {}, restrict: 'AE', require: '^outerDirective', link: function(scope, elem, attrs, controllerInstance) { scope.message = "Hi,I am Ruby."; controllerInstance.say(scope); } }; }); app.directive('innerDirective2', function() { return { scope: {}, restrict: 'AE', require: '^outerDirective', link: function(scope, elem, attrs, controllerInstance) { scope.message = "Hi,I am Hello World."; controllerInstance.say(scope); } }; }); </script> </body> </html>
require参数有四种:
1)没有前缀,指令会在自身提供的控制器中进行查找,若是找不到任何控制器,则会抛出一个error
2)?若是在当前的指令没有找到所需的控制器,则会将null传给link链接函数的第四个参数
3)^若是在当前的指令没有找到所需的控制器,则会查找父元素的控制器
4)?^组合
compile function
function compile(tElement, tAttrs, transclude) { ... }
编译函数用来修改模板dom的(不经常使用)
须要用到编译函数例如:ngTrepeat;ngView(须要异步载入内容的)。
各参数意义:
tElement - template element 该指令所在的元素。
tAttrs - template attributes 指令元素上所声明的属性。
transclude - 一个嵌入的连接函数function(scope, cloneLinkingFn)。
注意:在编译函数里面不要进行任何DOM变形以外的操做。 更重要的,DOM监听事件的注册应该在连接函数中作,而不是编译函数中。 编译函数能够返回一个对象或者函数。 返回函数 - 等效于在编译函数不存在时,使用配置对象的link属性注册的连接函数。 返回对象 - 返回一个经过pre或post属性注册了函数的对象。参考下面pre-linking和post-liking函数的解释。
link function
function link(scope, iElement, iAttrs, controller) { ... }
连接函数负责注册DOM事件和更新DOM。它是在模板被克隆以后执行的,它也是大部分指令逻辑代码编写的地方。
scope - 指令须要监听的做用域。
iElement - instance element - 指令所在的元素。只有在postLink函数中对元素的子元素进行操做才是安全的,由于那时它们才已经所有连接好。
iAttrs - instance attributes - 实例属性,一个标准化的、全部声明在当前元素上的属性列表,这些属性在全部连接函数间是共享的。
controller - 控制器实例,也就是当前指令经过require请求的指令someDirective内部的controller。好比:someDirective指令中的controller:function(){this.Say = function(){}},那么,在当前指令的link函数中,你就能够经过controller.Say进行调用了。
Pre-linking function 在子元素被连接前执行。不能用来进行DOM的变形,以防连接函数找不到正确的元素来连接。
Post-linking function 全部元素都被连接后执行。
compile function 和 link function须要注意的地方:二者是互斥的,compile function负责对模板dom进行修改,link function负责将做用域和dom进行链接。
当二者共存时,compile function返回的函数看成link function,link function则被忽略。