AngularJS 学习笔记 -- 指令(Directive)
AngularJS 指令学习笔记AngularJS怎样处理指令其实是依赖于指令定义时返回的对象属性的,所以要想深入理解如何定义一个指令,首相需要理解指令定义时各个参数的含义。 完整的AngularJS指令参数angular.module('app',[])
.directive('demoDirective',function (){ // 依据官方规范,指令的定义时应该严格遵循驼峰式命名规则,使用时采用'-'连接单词
return {
restrict : String in ['E','A','C','M'],priority : Number,terminal : Boolean,template : String or Template Function : function (tElement,tAttrs) {...},templateUrl : String,require : String or String Array,replace : Boolean or String,scope : Boolean or Object,transclude : false or element,controller : String or function (scope,element,attrs,transclude,otherInjectable) {...},controllerAs : String,link : function (scope,otherController) {...},compile : function (element,transclude) {
return object || function (...) {...}
}
};
});
上面的这么多参数如果按照功能划分的话,大致上可以分为如下三类: 内部参数·restrict :’E’ – Element,‘A’ – Attribute,’C’ – Class,‘M’ – comMent 对外参数scopescope 参数的作用是:隔离指令与所在控制器(父级作用域的控制器)间的作用域,隔离指令与指令之间的作用域。 关于scope可选值的不同结果对比: <!DOCTYPE html>
<html lang="zh_CN" ng-app="app">
<head>
<meta charset="UTF-8">
<title>AngularJS Index</title>
<script type="text/javascript" src="lib/jquery-1.12.3.js"></script>
<script type="text/javascript" src="lib/angular.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<style> .box { max-width: 680px; min-height: 80px; margin: 10px; padding: 10px 15px; border: solid 1px #2a3ca2; } .box span,.box input { display: block; } input[type="text"] { width: 280px; margin: 5px 0; } </style>
</head>
<body>
<div ng-controller="parentCtrl">
<h3>scope参数选择不同值时的作用域情况对比</h3>
<span>parent</span>
<div class="box" >
<span>{{parentName}}</span>
<input type="text" ng-model="parentName">
<span>{{scopeName}}</span>
<input type="text" ng-model="scopeName">
</div>
<span>scop=False</span>
<dir-f class="box"></dir-f>
<span>scope=True</span>
<dir-t class="box"></dir-t>
<span>scope={}</span>
<dir-o-n class="box"></dir-o-n>
<span>scope={attrs}</span>
<dir-o class="box"></dir-o>
</div>
</body>
</html>
app.js ;(function($) {
'use strict';
angular.module('app',[])
.controller('parentCtrl',['$scope',function($scope) {
$scope.parentName = 'parentName';
$scope.scopeName = 'scopeNameInParentScope';
}])
.directive('dirF',[function() {
// Runs during compile
return {
// name: '',
// priority: 1,
// terminal: true,
// scope: {},// {} = isolate,true = child,false/undefined = no change
// controller: function($scope,$element,$attrs,$transclude) {},
// require: 'ngModel',// Array = multiple requires,? = optional,^ = check parent elements
// restrict: 'A',// E = Element,A = Attribute,C = Class,M = Comment
// template: '',
// templateUrl: '',
// replace: true,
// transclude: true,
// compile: function(tElement,tAttrs,function transclude(function(scope,cloneLinkingFn){ return function linking(scope,elm,attrs){}})),
//link: function($scope,iElm,iAttrs,controller) {}
restrict: 'E',scope: false,replace: true,template: [
'<div>',' <span>parentName</span>',' <span>{{ parentName }}</span>',' <input type="text" ng-model="parentName" />',' <span>scopeName</span>',' <span>{{ scopeName }}</span>',' <input type="text" ng-model="scopeName" />','</div>'
].join(''),controller : ['$scope',function ($scope){
$scope.scopeName = '123456';
}]
};
}])
.directive('dirT',[function() {
return {
restrict: 'E',scope: true,controller: ['$scope',function ($scope) {
$scope.parentName = 'parentNameInDirectiveTrue';
//$scope.scopeName = 'scopeNameInDirectiveTrue';
// 在当前$scope中以已经生明的情况下,$scope.$watch监听的是自身的parentName
$scope.$watch('parentName',function(newValue,oblValue) {
console.log('directive-true -->> from watch scope parentName = ' + newValue);
});
// 监听父作用域中的parentName
$scope.$parent.$watch('parentName',oldValue) {
console.log('directive-true -->> from watch parent scope parentName = ' + newValue);
});
}]
};
}])
.directive('dirON',scope: {
},function ($scope) {
$scope.parentName = 'parentNameInDirectiveObjectWithNull';
//$scope.scopeName = 'scopeNameInDirectiveObjectWithNull';
// 在当前$scope中以已经生明的情况下,$scope.$watch监听的是自身的parentName
$scope.$watch('parentName',oblValue) {
console.log('directive-Object-Null -->> from watch scope parentName = ' + newValue);
});
// 监听父作用域中的parentName
$scope.$parent.$watch('parentName',oldValue) {
console.log('directive-Object-Null -->> from watch parent scope parentName = ' + newValue);
});
}]
}
}])
.directive('dirO',scope: {
parentName : '@'
},function ($scope) {
$scope.parentName = 'parentNameInDirectiveObject';
//$scope.scopeName = 'scopeNameInDirectiveObject';
// 在当前$scope中以已经生明的情况下,$scope.$watch监听的是自身的parentName
$scope.$watch('parentName',oblValue) {
console.log('directive-Object -->> from watch scope parentName = ' + newValue);
});
// 监听父作用域中的parentName
$scope.$parent.$watch('parentName',oldValue) {
console.log('directive-Object -->> from watch parent scope parentName = ' + newValue);
});
// 监听父作用域中的scopeName
$scope.$parent.$watch('scopeName',oldValue) {
console.log('directive-Object -->> from watch parent scope scopeName = ' + newValue);
});
}]
}
}]);
}(jQuery));
实验结果: scope取值为非空对象时属性扩展特性自定义指令中,当scope取值为非空对象时,指令会将该对象处理成子域scope的扩展属性。这一扩展属性肩负起了指令和父作用域通信的任务。 <div ng-controller="parentScopeCtrl">
<div class="box">
<p>parentScope :</p>
<span>id</span>
<span>{{ id }}</span>
<input type="text" ng-model="id">
<span>gender</span>
<span>{{ gender }}</span>
<input type="text" ng-model="gender">
<span>demo</span>
<span>{{ demo }}</span>
<input type="text" ng-model="demo">
<span>other</span>
<span>{{ other }}</span>
<input type="text" ng-model="other">
</div>
<!-- '@'和'='对应Attribute属性的值,'@'是单向绑定父域的机制,需要使用{{}}表达式;'&'对应的属性名必须要以on开头 -->
<dir-scope my-id="id" gender="gender" my-age="{{ age }}" on-speak="speak('demo')"></dir-scope>
app.js .controller('parentScopeCtrl',function ($scope){
$scope.id = 12345;
$scope.gender = "male";
$scope.age = 24;
$scope.demo = "haha";
$scope.other = "Other";
$scope.speak = function (msg){
console.log(msg);
}
}])
.directive('dirScope',[function(){
return {
restrict : 'EA',scope : {
myAge : '@',/* 子作用能感知到父作用域的变更,反之不行; 需要注意的是,属性赋值时需要使用{{}}表达式,而且该属性的值类型永远是String,也即是执行{{}}表达式返回的String */
myId : '=',/* 父子作用域双向绑定 */
myGender : '=gender',/* 父子作用域双向绑定; 与'='不同在于使用属性时的采用gender='' */
onSpeak : '&' // 通常&
},template : [
'<div>',' <span>myID</span>',' <span>{{ myId }}</span>',' <input type="text" ng-model="myId" />',' <span>myGender</span>',' <span>{{ myGender }}</span>',' <input type="text" ng-model="myGender" />',' <span>myAge</span>',' <span>{{ myAge }}</span>',' <input type="text" ng-model="myAge" />',' <span>demo</span>',' <span>{{ demo }}</span>',' <input type="text" ng-model="demo" />',' <span>other</span>',' <span>{{ other }}</span>',' <input type="text" ng-model="other" />',' <button ng-click="log()">Show Different</button>',function ($scope){
$scope.log = function (){
console.log(typeof $scope.myId,$scope.myId);
console.log(typeof $scope.myGender,$scope.myGender);
console.log(typeof $scope.myAge,$scope.myAge);
console.log(typeof $scope.onSpeak,$scope.onSpeak);
$scope.onSpeak();
}
$scope.demo = "scope-hahaha"; // 完全与父域中同名属性隔离
// 指令中使用other属性,但是由于scope取值为对象,所以也是与父域完全隔离的
// 在当前$scope中以已经生明的情况下,$scope.$watch监听的是自身的parentName
$scope.$watch('demo',oblValue) {
console.log('directive-scope -->> from watch scope demo = ' + newValue);
});
// 监听父作用域中的parentName
$scope.$parent.$watch('demo',oldValue) {
console.log('directive-scope -->> from watch parent scope demo = ' + newValue);
});
}]
};
}]);
参数require与scope参数一样,require参数也是指令与外界通信的媒介。scope参数主要负责的是指令与外界作用域间的通信,require参数主要负责指令与指令之间的通信。大部分自定义指令都很能独立完成某项复杂的任务,往往需要多个指令之间相互组合协作。 require参数接收一个String字符串或是一个字符串数组,String值为需要引入的外界指令的名称,实际传入的是外界指令对应的控制器(这个后续再讨论link参数时详细讨论)。require参数在寻找依赖指令时提供了两种策略’?’ 和 ‘^’。 行为参数link与controllerlink与controller参数都是描述指令行为的参数,但他们两分别负责不同的行为描述。 link参数理解从AngularJS官方教程上我们可以获知,Angular在刚从Server Response中获取得到静态网页时,首先回去扫描整个页面并收集HTML页面中包含哪些指令(Angular原生的还是用户自定义的),然后再去加载指令的template中的HTML模板或是下载templateUrl中指定的模板,如此类推,如果加载进来的HTML模板中包含其他指令,继续上诉操作,最终形成模板树,并返回相应的模板函数,提供给下一阶段进行数据绑定。简单的应用示例; <body ng-app='app'>
<dir-demo></dir-demo> <script > angular.module('app',[]) .directive( 'dirDemo',function () { return { restrict: 'E',template: '<p>dirDemo</p><dirDemo2></dirDemo2>',link: function (scope) { console.log( 'dirDemo2' ); } }; }) .directive( 'dirDemo2',template: '<p>dirDemo2</p><dirDemo3></dirDemo3>',link: function (scope) { console.log( 'dirDemo2' ); } }; }) .directive( 'dirDemo3',template: '<p>dirDemo3</p>',link: function (scope) { console.log( 'dirDemo3' ); } }; }); </script > </ body>
运行上述程序观察输出: dirDemo, dirDemo2, dirDemo3 angular.module('app',[])
.directive( 'dirDemo',function () {
return {
restrict: 'E',require : '^ngModel',// 需要引用外界指令ngModel的控制器
template: '<p>dirDemo</p><dirDemo2></dirDemo2>',link: function ($scope,ctrl) {
$element.bind("click",function () {
console.log("绑定点击事件");
});
$element.append("<p>增加段落块</p>");
//设置样式
$element.css("background-color","yellow");
//最佳实践中,应该下面的方法转移到controller中
$scope.hello = function () {
console.log("hello");
};
}
};
});
link函数的参数是固定的(
如果我们在上面的示例代码中添加上controller后,在此进行断点调试可发现,全局的执行顺序是: compile参数我们通过定义一个compile来取代link函数。可以说compile提供了一个更细粒度的link函数形式,在compile函数中我们可以使用pre-link和post-link函数来替代link函数。 <level-one>
<level-two>
<level-three>Hello </level-three>
</level-two>
</level-one>
app.js function createDirective(name){
return function(){
return {
restrict: 'E',compile: function(tElem,tAttrs){
console.log(name + ': compile');
return {
pre: function(scope,iElem,iAttrs){
console.log(name + ': pre-link');
},post: function(scope,iAttrs){
console.log(name + ': post-link');
}
}
}
}
}
};
angular.module('app',[])
.directive('levelOne',createDirective('levelOne'))
.directive('levelTwo',createDirective('levelTwo'))
.directive('levelThree',createDirective('levelThree'));
运行上面的实例程序,我们能够简单的了解到AngularJS在处理指令时的内部流程。 levelOne: compile
levelTwo: compile
levelThree: compile
levelOne: pre-link levelTwo: pre-link levelThree: pre-link levelThree: post-link levelTwo: post-link levelOne: post-link
从上面的示例中我们可以发现,link过程,其实还分为pre-link和post-link阶段。同时Angular在指令的link函数调用前先调用了指令的compile函数对指令进行了编译。 关于compile的更多内容将在下一篇文章中进行分享。 参考文献 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |