指令的参数如下:
angular.module('app', [])
.directive('myDirective', function() {
return {
restrict: String,
//规定指令在HTML代码中可以使用什么表现形式。
//A代表属性、E代表元素、C代表类、M代表注释。默认用AE这两种方式。
priority: Number,
//规定自定义的指令的优先级的,当一个DOM元素上面有一个以上的指令的时候,
//就需要去比较指令的优先级了,优先级高的指令先执行。
//这个优先级就是用来在执行指令的compile函数前,先排序的
terminal: Boolean,
//定义是否停止当前元素上比本指令优先级低的指令,
//如果值为true,就是正常情况,按照优先级高低的顺序来执行,
//如果设置为false,就不会执行当前元素上比本指令优先级低的指令。
template: String or Template Function:
function(tElement, tAttrs) {...},
//指令被Angular编译和链接(link)后生成的HTML标记.
//这个属性可以简单到只有一个HTML文本在里面,也可以特别复杂,
//当该属性的值为function的时候,那么该方法返回的就是代表模板的字符串,
//同时也可以在里面使用{{}}这个表达式。
//但是在一般情况下,template这个属性都会被templateUrl取代掉,
//用它来指向一个外部的文件地址,所以我们通常把模板放在外部的一个HTML文件中,
//然后使用templateUrl来指向他。
templateUrl: String,
//html的url
replace: Boolean or String,
//规定生成的HTML内容是否会替换掉定义此指令的HTML元素,默认false。(自定义的指令名称是否替换,true替换,false不替换)
scope: Boolean or Object,
//指令与外界作用域通讯的桥梁
transclude: Boolean,
//规定指令是否可以包含任意内容.默认false。(之前的内容是否保留,true保留,false不保留,ng-tranclude决定了在什么地方放置嵌入部分。)
controller: String or
function(scope, element, attrs, transclude, otherInjectables) { ... },
//设置各个作用域scope
//当我们想要允许其他的指令和你的指令发生交互时,我们就需要使用 controller 函数。
//当另一个指令想要交互时,它需要声明它对你的指令 controller 实例的引用(require)。
controllerAs: String,
require: String,//指令与指令之间通讯的桥梁
link: function(scope, iElement, iAttrs) { ... },
//描述指令元素操作行为
//link函数,它包括三个参数:scope、element、attrs。
//这个link函数主要是用来添加对DOM元素的事件监听、监视模型属性变化、以及更新DOM的。
//它里面三个参数:
//一:scope参数,在我们没有为指令定义scope属性的时候,
//那么他代表的就是父controller的scope。
//二:element参数,就是指令的jQLite(jQuery的子集)包装DOM元素。
//如果你在引入AngularJS之前引入了jQuery,那么这个元素就是jQuery元素,而不是jQLite元素。
//由于这个元素已经被jQuery/jQLite包装了,
//所以我们就在进行DOM操作的时候就不需要再使用 $()来进行包装。
//三:attrs参数,它包含了该指令所在元素的属性的标准化参数对象。
compile:
//该方法有两个参数element,attrs,
//第一个参数element指指令所在的元素,
//第二个attrs指元素上赋予的参数的标准化列表。
//这里我们也有个地方需要注意:compile 函数不能访问 scope,并且必须返回一个 link 函数。
//但是如果没有设置 compile函数,你可以正常地配置 link函数,
//(有了compile,就不能用link,link函数由compile返回)。
// 返回一个对象或连接函数,如下所示:
function(tElement, tAttrs, transclude) {
return {
pre: function(scope, iElement, iAttrs, controller) { ... },
post: function(scope, iElement, iAttrs, controller) { ... }
}
return function postLink(...) { ... }
}
};
});
scope:
scope参数的作用是,隔离指令与所在控制器间的作用域、隔离指令与指令间的作用域。
是否独立作用域,作用域是否父子影响。
scope参数是可选的,默认值为false,可选true、对象{};
false:共享父域
true:继承父域,且新建独立作用域
对象{}:不继承父域,且新建独立作用域,为空不影响父子作用域数据,不为空有影响父子作用域数据。
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>My AngularJS App</title>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular.min.js"></script>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular-route.min.js"></script>
</head>
<!--scope扩展对象,既能够解耦父域与子域共域的问题,
也能够实现指令与外界通讯的问题,是Angular开发指令化模块化的重要基础。-->
<body>
<div ng-controller='parentCtrl'>
<h3>指令scope参数——false、true、{}对比测试</h3>
parent:
<div>
<span> {{parentName}}</span>
<input type="text" ng-model="parentName"/>
</div>
<br/>
<child-a></child-a>
<br/>
<child-b></child-b>
<br/>
<child-c parent-name="parentName"></child-c>
</div>
<!--t1指令模板-->
<script type="text/html" id="t1">
<div>
<span>{{parentName}}</span>
<input type="text" ng-model="parentName"/>
</div>
</script>
<script>
var app = angular.module("app", [])
.controller('parentCtrl', function ($scope) {
$scope.parentName = "parent";
})
//false:共享作用域(互相影响)
.directive('childA', function () {
return {
restrict: 'E',
scope: false, /*不独立*/
template: function (elem, attr) {
console.log('内容:', document.getElementById('t1').innerHTML);
/*获取到js中包括div的内容*/
return "scope:false:共享作用域(互相影响):" + document.getElementById('t1').innerHTML;
}
};
})
//true:继承父域,并建立独立作用域(互不影响)
.directive('childB', function () {
return {
restrict: 'E',
scope: true, /*独立*/
/*模板HTML*/
template: function (elem, attr) {
return "scope:true:继承父域,并建立独立作用域:" + document.getElementById('t1').innerHTML;
},
/*controller本身的意义就是赋予指令控制器,而控制器就是定义其内部作用域的行为的。
所以controller要描述的是:指令的作用域的行为。*/
controller: function ($scope) {
$scope.parentName = "parent";
//已声明的情况下,$scope.$watch监听的是自己的parentName
$scope.$watch('parentName', function (n, o) {
console.log(" scope: true==child watch" + n);
});
//$scope.$parent.$watch监听的是父域的parentName
$scope.$parent.$watch('parentName', function (n, o) {
console.log(" scope: true==parent watch" + n);
});
}
};
})
//{}:不继承父域,建立独立作用域
.directive('childC', function () {
return {
restrict: 'E',
scope: {}, /*独立*/
template: function (elem, attr) {
return "scope:{}:不继承父域,建立独立作用域:" + document.getElementById('t1').innerHTML;
},
controller: function ($scope) {
console.log($scope);
//已声明的情况下,$scope.$watch监听的是自己的parentName
$scope.$watch('parentName', function (n, o) {
console.log(" scope: {}==child watch" + n);
});
//$scope.$parent.$watch监听的是父域的parentName
$scope.$parent.$watch('parentName', function (n, o) {
console.log(" scope: {}==parent watch" + n);
});
}
};
});
</script>
</body>
</html>
1、当scope对象为非空对象时,指令会将该对象处理成子域scope的扩展属性。
2、而父域与子域之间传递数据的任务,就是可以通过这块扩展属性完成。
3、不继承父域,建立独立作用域,我们使用了隔离的作用域,不代表我们不可以使用父作用域的属性和方法。
// 继承父域,并建立独立作用域
scope: true,
// 继承父域,共享作用域
scope: false,
// 不继承父域,建立独立作用域,我们使用了隔离的作用域,不代表我们不可以使用父作用域的属性和方法。
//<div class="my-directive" my-directive my-name="{{name}}" age="age" change-my-age="changeAge()"></div>
// @: 单向数据绑定父域,要通过使用{{}}来绑定数据。
// =:双向数据绑定
// &:绑定父域的函数方法
// 注意,属性的名字要用-将多个个单词连接。
scope: {
// `myName` 就是原来元素中的`my-name`属性
age: '=',
changeAge: '&changeMyAge'
// `changeMyAge`就是原来元素中的`change-my-age`属性
},
注意:scope参数的定义和使用:
例子如下:
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>My AngularJS App</title>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular.min.js"></script>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular-route.min.js"></script>
</head>
<!--scope扩展对象,既能够解耦父域与子域共域的问题,
也能够实现指令与外界通讯的问题,是Angular开发指令化模块化的重要基础。-->
<body>
<div ng-controller='parentCtrl'>
parent:
<p><span>{{name}}</span><input type="text" ng-model="name"/></p>
<p><span>{{sexy}}</span><input type="text" ng-model="sexy"/></p>
<p><span>{{age}}</span><input type="text" ng-model="age"/></p>
<br/>
<!--特别注意:@与=对应的attr,@是单向绑定父域的机制,记得加上{{}};&对应的attrName必须以on-开头-->
<child-c my-name="name" my-sexy-attr="sexy" my-age="{{age}}" on-say="say('i m ' + name)"></child-c>
<!-- =Attr 需要名称小写并加上:-attr -->
<!-- =策略不需要加上{{}}进行绑定 -->
<!-- @ 是单向绑定本地作用域,记得加上{{}} -->
<!-- &对应的attrName必须以on-开头 -->
</div>
<!--t1指令模板-->
<script type="text/html" id="t1">
<div>
<span>=双向:{{myName}}</span>
<input type="text" ng-model="myName"/>
</div>
<div>
<span>=mySexyAttr双向:{{mySexy}}</span>
<input type="text" ng-model="mySexy"/>
</div>
<div>
<span>@单向:{{myAge}}</span>
<input type="text" ng-model="myAge"/>
</div>
</script>
<script>
var app = angular.module("app", []);
app.controller('parentCtrl', function ($scope) {
$scope.name = "mark";
$scope.sexy = "male";
$scope.age = "30";
$scope.say = function (sth) {
alert(sth);
};
})
app.directive('childC', function () {
return {
restrict: 'E',
/*2、当scope对象为非空对象时,指令会将该对象处理成子域scope的扩展属性。
而父域与子域之间传递数据的任务,就是可以通过这块扩展属性完成。*/
/*独立作用域,不为空时影响数据*/
scope: {
/* = (or =Attr)绑定策略——双向绑定:通过=可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。
就像普通的数据绑定一样,本地属性会反映出父数据模型中所发生的改变。(双向引用父域对象)*/
myName: '=',
mySexy: '=mySexyAttr',
/* @(or @Attr)绑定策略——本地作用域属性,
使用@符号将本地作用域同DOM属性的值进行绑定。
指令内部作用域可以使用外部作用域的变量。(单向引用父域对象)*/
myAge: '@',
/* &(or &Attr)绑定策略——通过&符号可以对父级作用域进行绑定,以便在其中运行函数。(调用父域函数)
* &对应的attrName必须以on-开头*/
onSay: '&'
},
template: function (elem, attr) {
console.log('内容:', document.getElementById('t1').innerHTML);
return "scope:{}:" + document.getElementById('t1').innerHTML;
},
controller: function ($scope) {
console.log($scope.myName);
/*mark*/
console.log($scope.mySexy);
/*male*/
console.log($scope.myAge);
/*30*/
$scope.onSay();
}
};
});
</script>
</body>
</html>
require
scope是指令与外界作用域通讯的桥梁,而require是指令与指令之间通讯的桥梁。
这个参数最大的作用在于,当要开发单指令无法完成,需要一些组合型指令的控件或功能,
例如日期控件,通过require参数,指令可以获得外部其他指令的控制器,
从而达到交换数据、事件分发的目的。
使用方法:require: String or Array——String值为引入指令名称,
并且有两个寻找指令策略符号‘?’与‘^’;Array数组则为多个外部指令名称。
在link函数第4个参数ctrl中获取注入外部指令的控制器,
如果require为String,ctrl为对象,如果require是数组,ctrl为数组。
require: ‘^teacher1’, link: function ( scope, element, $attrs,
ctrl) {
//ctrl指向teacher1指令的控制器 }
?策略——寻找指令名称,如果没有找到,link函数第4个参数为null;如果没有?,则报错。
^ 策略——在自身指令寻找指令名称的同时,向上父元素寻找;如果没有^,则仅在自身寻找。
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>My AngularJS App</title>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular.min.js"></script>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular-route.min.js"></script>
</head>
<!--scope是指令与外界作用域通讯的桥梁,而require是指令与指令之间通讯的桥梁。
这个参数最大的作用在于,当要开发单指令无法完成,需要一些组合型指令的控件或功能,
例如日期控件,通过require参数,指令可以获得外部其他指令的控制器,
从而达到交换数据、事件分发的目的。-->
<!--使用方法:require: String or Array——String值为引入指令名称,
并且有两个寻找指令策略符号‘?’与‘^’;Array数组则为多个外部指令名称。
在link函数第4个参数ctrl中获取注入外部指令的控制器,
如果require为String,ctrl为对象,如果require是数组,ctrl为数组。
require: '^teacher1',
link: function ($scope, $element, $attrs, ctrl) {
//ctrl指向teacher1指令的控制器
}
?策略——寻找指令名称,如果没有找到,link函数第4个参数为null;如果没有?,则报错。
^ 策略——在自身指令寻找指令名称的同时,向上父元素寻找;如果没有^,则仅在自身寻找。
-->
<body>
<div teacher>
{{name}}
<student-a></student-a>
<student-b></student-b>
</div>
<script>
var app = angular.module("app", [])
//studentA——require指向父级指令teacher
.directive('studentA', function () {
return {
require: '?^teacher',/*指令间数据传递*/
scope: {},/*指令与外界作用域的数据传递:独立,父子数据互不影响*/
template: '<div>A`s teacher name: <span>{{teacherName}}</span></div>',/*视图模板*/
link: function ($scope, $element, $attrs, ctrl) {/*链接函数:描述指令元素操作行为*/
//获取teacher指令控制器,并调用其方法sayName()
$scope.teacherName = ctrl.sayName(); //ctrl指向teacher1指令的控制器
}
};
})
//studentB——require指向父级指令teacher,及指令studentA
//但是,由于不能获得兄弟,也没有采取?策略,导致报错
.directive('studentB', function () {
return {
require: ['?^teacher', 'studentA'],
scope: {},
template: '<div>B`s teacher name: <span>{{teacherName}}</span></div>',
link: function ($scope, $element, $attrs, ctrl) {
$scope.teacherName = ctrl.sayName();
}
};
})
.directive('teacher', function () {
return {
restrict: 'A',
controller: function ($scope) {/*指令的作用域的行为。*/
$scope.name = "Miss wang";
//扩展控制器的方法sayName,目的是让外部内获取控制器内部数据
this.sayName = function () {
return $scope.name;
};
}
};
});
</script>
</body>
</html>
不添加问号,报错:
require: [‘?^teacher’, ‘studentA’],
添加问号,但找不到报错:
require: [‘?^teacher’, ‘?studentA’],
link
Angular在刚从HTTP Response接收静态素材之初,会首先去分析母页HTML中有哪些原生指令或自定义指令,
然后再去加载指令的template模板HTML,而template模板中又去加载自己的指令模板,
如此类推,直到Angular找到了所有的指令及模板,形成模板树,并返回模板函数,提供给下一阶段进行数据绑定。
简单来说就是:
1、执行controller,设置各个作用域scope
2、加载模板,形成DOM模板树
3、执行link,设置DOM各个行为
4、数据绑定,最后scope绑上DOM
在这个时间节点的link函数,操作DOM的性能开销是最低,非常适合在这个时机执行DOM的操作,
例如鼠标操作或触控事件分发绑定、样式Class设置、增删改元素等等。
所以link就是描述指令元素操作行为。
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>My AngularJS App</title>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular.min.js"></script>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular-route.min.js"></script>
</head>
<!--Angular在刚从HTTP Response接收静态素材之初,会首先去分析母页HTML中有哪些原生指令或自定义指令,
然后再去加载指令的template模板HTML,而template模板中又去加载自己的指令模板,
如此类推,直到Angular找到了所有的指令及模板,形成模板树,并返回模板函数,提供给下一阶段进行数据绑定。-->
<!--简单来说就是:
1、执行controller,设置各个作用域scope
2、加载模板,形成DOM模板树
3、执行link,设置DOM各个行为
4、数据绑定,最后scope绑上DOM
在这个时间节点的link函数,操作DOM的性能开销是最低,非常适合在这个时机执行DOM的操作,
例如鼠标操作或触控事件分发绑定、样式Class设置、增删改元素等等。
所以link就是描述指令元素操作行为。-->
<body>
<stu1></stu1>
<script>
var app = angular.module("app", []);
app.directive('stu1', function () {
return {
restrict: 'E',
template: "<p>1</p><stu2></stu2>",
link: function (scope) {
console.log('stu1 running');
}
};
});
app.directive('stu2', function () {
return {
restrict: 'E',
template: "<p>2</p><stu3></stu3>",
link: function (scope) {
console.log('stu2 running');
}
};
});
app.directive('stu3', function () {
return {
restrict: 'E',
template: "<p>3</p>",
link: function (scope) {
console.log('stu3 running');
}
};
});
</script>
</body>
</html>
controller与link的执行顺序是先controller,后link。
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>My AngularJS App</title>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular.min.js"></script>
<script src="http://sandbox.runjs.cn/uploads/rs/376/pbcx3e1z/angular-route.min.js"></script>
</head>
<body>
<div student>
{{name }}
</div>
<script>
var app = angular.module("app", []);
app.directive('student', function () {
return {
restrict: 'A',
controller: function ($scope) {/*设置scope*/
$scope.name = "tgor";
console.log('controller running');//先执行
},
link: function (scope, el) {/*描述指令元素操作行为*/
el.append("<p>hello</p>");
console.log('link running');//后执行
}
};
});
</script>
</body>
</html>
replace
顾名思义,这是替换的意思,默认为 false,就是将模版的内容追加到元素中,如果设置为 true,那么模版的内容将会替换元素的内容。
<!-- 元素 -->
<div>
<hello></hello>
</div>
<!-- 属性-->
<div>
<div hello=""></div>
</div>
<!-- class -->
<div>
<div class="hello"></div>
</div>
如果我们将模版更新为一个元素,如下所示。
var app = angular.module("app", [])
.directive("hello", function () {
var option = {
restrict: "AECM",
template: "<h3>Hello, Directive</h3>",
replace: true
};
return option;
})
这次,你将会看到在 replace 为 true 情况下的元素了。
<!-- 元素 -->
<div>
<h3>Hello, Directive</h3>
</div>
<!-- 属性-->
<div>
<h3 hello="">Hello, Directive</h3>
</div>
<!-- class -->
<div>
<h3 class="hello">Hello, Directive</h3>
</div>
transclusion:嵌入包含
<!-- 元素 -->
<div>
<hello>12345678</hello>
</div>
如果只replace,你会发现 12345678 消失了,这个元素完全被模版替换了。如果我们需要保留这些内容怎么处理呢?
通过transclude属性移动原始的内容到新模版中,当设置成为true时,标识符会删除原来的内容,并通过ng-transclude标识符使它重新插入到模版中.
transclusion指的是定义模版的元素如何处理问题,比如,在使用指令的时候,指令中包含了内容,那么这些内容我们是否直接被替换为模版,还是将这些内容嵌入到模版中。
在使用它的时候,需要在两个地方说明,一是在指令中说明需要嵌入,二是在模版中说明嵌入到哪里。
var app = angular.module("app", [])
.directive("hello", function () {
var option = {
restrict: "AECM",
template: "<h3>Hello, Directive, <span ng-transclude></span></h3>",
replace: true,
transclude: true
};
return option;
})
然后,在模版中说明嵌入的位置。
template: "
<h3>
Hello, Directive,
<span ng-transclude></span>
</h3>",
页面中的使用。
<hello>12345678</hello>
最后,生成的结果如下。
<h3>Hello, Directive,
<span ng-transclude="">
<span class="ng-scope">12345678</span>
</span>
</h3>
将视图模板(Template或TemplateUrl)替换到指定位置的视图(Restrict),
replace:自定义指令名称是否保留。
true:不保留指令名
false:保留指令名(默认)
Transclude:是否将原来视图的内容嵌入到视图模板(Template或TemplateUrl)中。
true:保留替换前的节点内容。
false:直接覆盖原有内容。
ng-tranclude决定了在什么地方放置嵌入部分。
参考: