Angular 指令好文章收录(三)
指令,很重要AngularJS与JQuery最大的区别在哪里?我认为,表现在数据双向绑定,实质就是DOM的操作形式不一样。
所以AngularJS开发最理想的结果就是,在页面HTML与CSS的设计时,设计工程师只需要关注指令的使用;而在背后的逻辑开发上,架构工程师则是不需要知道如何操作DOM,只需要关注指令背后的行为要如何实现就行;测试工程师也可以开发针对指令的单元测试。 指令就是DOM与逻辑行为的媒介,本质就是DOM绑定的独立逻辑行为函数。 指令难点在于参数来看看都有哪些 angular.module('app',[])
.directive('myDirective',function() {
return {
restrict: String,priority: Number,terminal: Boolean,template: String or Template Function:
function(tElement,tAttrs) {...},templateUrl: Boolean or Object,transclude: String or
function(scope,element,attrs,transclude,otherInjectables) { ... },controllerAs: require: // 返回一个对象或连接函数,如下所示:
return {
pre: return function postLink(...) { ... }
}
};
});
刚开始接触指令的时候,我简直就是蒙了,这堆参数究竟怎么用怎么理解啊。告诉大家我的一个理解方法。
内部参数
对外参数——scopescope参数非常重要,本应该是放到最后说明的,但是scope却是理解其他参数的关键,所以务必先跟大家说清楚。 scope参数的作用是,隔离指令与所在控制器间的作用域、隔离指令与指令间的作用域。 scope参数是可选的,默认值为false,可选true、对象{};
false、true、{}三者对比 来看个例子 <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>
<child-b></child-b>
<child-c parent-name="parentName"></child-c>
</div>
<!--t1指令模板-->
<script "text/html" id="t1"> <div> <span>{{parentName}}</span> <"parentName" /> </div> </script>
<script> var app = angular.module("app",[]); app.controller('parentCtrl',0)">function ($scope) { $scope.parentName = "parent"; }) //false:共享作用域 app.directive('childA',0)">function () { return { restrict: 'E',scope: false,template: function (elem,attr) { return "false:" + document.getElementById('t1').innerHTML; } }; }); //true:继承父域,并建立独立作用域 app.directive('childB',scope: true,152)">"true:" + 't1').innerHTML; },controller: "parent"; //已声明的情况下,$scope.$watch监听的是自己的parentName $scope.$watch('parentName',0)">function (n,o) { console.log("child watch" + n); }); //$scope.$parent.$watch监听的是父域的parentName $scope.$parent.$watch("parent watch" + n); }); } }; }); //{}:不继承父域,建立独立作用域 app.directive('childC',scope: {},152)">"{}:" + function ($scope) { console.log($scope); } }; }); </script>
</body>
本质:子域与父域共享作用域。
反之,指令内部parentName被修改时,父域的parentName同样会被刷新。
本质:子域继承父域,并建立独立作用域。 1、在指令已声明parentName的情况下,父域parentName变更,指令中parentName不会发生变化。 controller: function ($scope) {
$scope.parentName = "parent";
}
反之,指令中parentName变更,父域也不会发生变化。 2、在指令未声明parentName的情况下,父域的parentName变更,指令中parentName也会刷新 $scope) {
//$scope.parentName = "parent";
}
然而,指令中parentName变更,父域是不会变化的; 3、在指令已声明parentName的情况下 ,在指令中监听父域parentName 的变化无效。但监听子域parentName的变化有效 "parent" ;
//已声明的情况下,$scope.$watch监听的是自己的parentName
$scope.$watch( "child watch" + n);
});
//$scope.$parent.$watch监听的是父域的parentName
$parent."parent watch" + n);
});
}
4、在指令未声明parentName的情况下 ,在指令中监听父域parentName的变化有效。 //$scope.parentName = "parent";
//未声明的情况下,$scope.$watch监听的是父域的parentName
$watch("child watch" + n);
});
}
本质:子域不继承父域,并建立独立作用域。 1、当scope对象为空对象时,无论是父域parentName,还是指令子域parentName发生变更,都不会影响到对方。 scope: {}
2、当scope对象为非空对象时,指令会将该对象处理成子域scope的扩展属性。而父域与子域之间传递数据的任务,就是可以通过这块扩展属性完成。 'parentCtrl'>
parent:
<p><span>{{name}}</span><"name" /></p>
<span>{{sexy}}</"sexy" /></span>{{age}}</"age" /></br />
<!--特别注意:@与=对应的attr,@是单向绑定父域的机制,记得加上{{}};&对应的attrName必须以on-开头-->
<my-name="name" my-sexy-attr="sexy" my-age="{{age}}" on-say="say('i m ' + name)"></child-c>
</div>
<!--t1指令模板-->
<span>{{myName}}</"myName" /> </div> <span>{{mySexy}}</"mySexy" /> </span>{{myAge}}</"myAge" /> </script>
<function ($scope) { $scope.name = "mark"; $scope.sexy = "male"; $scope.age = "30"; $scope.say = function (sth) { alert(sth); }; }) app.directive('=',mySexy: '=mySexyAttr',myAge: '@',onSay: '&' },210)">console.log($scope.myName); console.log($scope.mySexy); console.log($scope.myAge); $scope.onSay(); } }; }); </script>
@(or @Attr)绑定策略——本地作用域属性,使用@符号将本地作用域同DOM属性的值进行绑定。指令内部作用域可以使用外部作用域的变量。(单向引用父域对象) "{{age}}"></child-c>
ps:@是单向绑定本地作用域,记得加上{{}} scope: { myAge: '@',}
= (or =Attr)绑定策略——双向绑定:通过=可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。就像普通的数据绑定一样,本地属性会反映出父数据模型中所发生的改变。(双向引用父域对象) onSay="name"></ ps:=策略不需要加上{{}}进行绑定
ps特别注意:@与=对应的attr,; <child-c my-name="name" my-sexy-attr="sexy" my-age="{{age}}" on-say="say('i m ' + name)"></child-c>
总结下来,scope扩展对象,既能够解耦父域与子域共域的问题,也能够实现指令与外界通讯的问题,是Angular开发指令化模块化的重要基础。在往后的章节,我会向大家介绍指令化开发的更多实例。 对外参数——requirescope是指令与外界作用域通讯的桥梁,而require是指令与指令之间通讯的桥梁。这个参数最大的作用在于,当要开发单指令无法完成,需要一些组合型指令的控件或功能,例如日期控件,通过require参数,指令可以获得外部其他指令的控制器,从而达到交换数据、事件分发的目的。 使用方法: 在link函数第4个参数ctrl中获取注入外部指令的控制器,如果require为String,ctrl为对象,如果require是数组,ctrl为数组。 require: '^teacher1',0)">$scope,$element,0)">$attrs,ctrl) {
//ctrl指向teacher1指令的控制器
}
?策略——寻找指令名称,如果没有找到,link函数第4个参数为null;如果没有?,则报错。 ^策略——在自身指令寻找指令名称的同时,向上父元素寻找;如果没有^,则仅在自身寻找。 <div teacher>
<student-a></student-a>
<student-b></student-b>
</div>
完整例子 teacher>
{{name}}
<student-a></student-a>
<student-b></student-b>
</div>
<//studentA——require指向父级指令teacher app.directive('studentA',0)">return { require: '?^teacher',template: '<div>A`s teacher name: <span>{{teacherName}}</span></div>',0)">function ($scope,$element,$attrs,ctrl) { //获取teacher指令控制器,并调用其方法sayName() $scope.teacherName = ctrl.sayName(); } }; }); //studentB——require指向父级指令teacher,及指令studentA //但是,由于不能获得兄弟,也没有采取?策略,导致报错 app.directive('studentB',210)">require: ['studentA'],152)">'<div>B`s teacher name: <span>{{teacherName}}</span></div>',ctrl) { $scope.teacherName = ctrl.sayName(); } }; }); app.directive('teacher',152)">'A',152)">"Miss wang"; //扩展控制器的方法sayName,目的是让外部内获取控制器内部数据 this.sayName = return $scope.name; }; } }; }); </body>
既然require可以获取外部指令,那Angular原生指令应该也是能够获取。其中最广泛应用的就是 行为参数——link与controller 为什么要把link与controller两个参数放到一起? link与controller都是描述指令行为的参数,但它们是要描述的行为是完全不同的类型。
controller本身的意义就是赋予指令控制器,而控制器就是定义其内部作用域的行为的。 所以controller要描述的是:指令的作用域的行为。 //指向匿名控制器
controller: $scope) {
},//指向控制器mainCtrl
controller: "mainCtrl"
link名称是链接函数,啥意思,好像挺难理解。所以在解释链接函数之前,先要说一下Angular的初始化对于指令究竟做了什么。 Angular在刚从HTTP Response接收静态素材之初,会首先去分析母页HTML中有哪些原生指令或自定义指令,然后再去加载指令的template模板HTML,而template模板中又去加载自己的指令模板,如此类推,直到Angular找到了所有的指令及模板,形成模板树,并返回模板函数,提供给下一阶段进行数据绑定。 stu1 ></ stu1>
<script > 'stu1',152)">"<p>1</p><stu2></stu2>",0)">function (scope) { console.log( 'stu1 running' ); } }; }); app.directive( 'stu2',152)">"<p>2</p><stu3></stu3>",152)">'stu2 running' ); } }; }); app.directive( 'stu3',152)">"<p>3</p>",152)">'stu3 running' ); } }; }); </script > </ body> console output stu3 running stu2 running stu1 running
注意以上例子,在第一个断点 简单来说就是:
@@@@是啥?没错,就是link链接函数,它会在形成模板树之后,在数据绑定之前,从最底部指令开始,逐个指令执行它们的link函数。 在这个时间节点的link函数,操作DOM的性能开销是最低,非常适合在这个时机执行DOM的操作,例如鼠标操作或触控事件分发绑定、样式Class设置、增删改元素等等。 所以link就是描述指令元素操作行为。 link: function (scope,attr,ctrl) {
element.bind("click",0)">function () { "绑定点击事件"); }); element.append("<p>增加段落块</p>"); //设置样式 element.css("background-color",152)">"yellow"); //不推荐,在link中赋予scope行为 scope.hello = "hello"); }; }
同理,在link中定义$scope行为是不推荐的。 这样想想,对于controller与link,就明白了。但还有一个问题,它们俩的执行顺序是?答案是先controller,后link。 放到全局顺序就是:
例子 student>
{{name }}
</'student',152)">"tgor"; 'controller running'); },0)">function (scope,el) { el.append("<p>hello</p>"); 'link running'); } }; }); </
|