AngularJs $compile编译服务与指令
$compile
这是个编译服务。编译一段HTML字符串或者DOM的模板, 产生一个将scope和模板连接到一起的函数。
编译服务主要是为指令编译DOM元素,下面的一大段也是主要介绍指令的。
下面是一个被声明的带指令定义对象的指令的示例:
var myModule = angular.module(...); myModule.directive('directiveName', [“injectables”,…,function factory(injectables,…) { var directiveDefinitionObject = { priority: 0, template: '<div></div>', // or // function(tElement, tAttrs) { ... }, // or // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, replace: false, transclude: false, restrict: 'A', scope: false, controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, controllerAs: 'stringAlias', require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } // or // return function postLink( ... ) { ... } }, // or // link: { // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, // post: function postLink(scope, iElement, iAttrs, controller) { ... } // } // or // link: function postLink( ... ) { ... } }; return directiveDefinitionObject; });
这是一个完整的指令,所返回的对象的各项属性都写在上面了,下面对于指令对象属性的介绍,就不一一的写示例代码了,后面主要写下简单的就ok。
备注:任何为指定的参数都将设置为默认值。下面将会列出各默认值。
指令定义对象
指令定义的对象为编译器提供说明,其属性是:
priority
当有一个DOM元素中定义了多个指令,有时有必要指定的指令的应用顺序。在编译函数被调用之前,先将其按优先级排序。这个属性的值越大,优先级越高,将会更先被编译。具有相同优先级的指令的顺序是未定义的。默认优先级为0。
如果该属性设置为true,那么当前指令的优先将会放在最后一组将执行(在当前优先级的任何指令将仍然作为未定义优先级的设置执行)
scope
如果设置为false,则不会为指令创建任何scope。指令使用其父级scope。
如果设置为true,那么该指令将会创建一个新的子scope,这个scope继承父级的属性。如果在一个DOM的多个指令上定义新作用域,也只能创建一个新的作用域。模板有了新的作用域后,该作用域的规则不适用与模板所在的根节点。
如果设置为{},那么将会创建一个新的、隔离的作用域。该作用域不继承其父作用域,这样就不会在创建可重用的指令是影响到父作用域的数据。
这个隔离的作用域会有一组来自父作用域的数据组成的对象,该对象名称前缀为:
@ or @attr : 结合局部作用域属性的DOM元素上的属性值。在DOM元素上和指令scope中以string类型绑定。如果没指定名称,那么与假定的指令作用域中的名称相同。
= or =attr:在指令作用域和父作用域之间设置数据的双向绑定。如果没指定名称,那么与假定的指令作用域中的名称相同。
& or &attr:提供了在父作用域的上下文中执行的表达式。
controller
该指令的控制器构造函数。控制器将会在预编译之前被实例化,并且可与其他指令共享(参见require属性)。这实现了指令之间的通信,增强了相互之间的行为。可写入以下参数:
$scope:与元素相关联的当前作用域。
$element:当前元素。
$attr:当前元素上的属性。
$transclude:一个被预绑定到正确的嵌入范围的linking函数。该范围可以通过一个第一个可选的参数重写。function([scope], cloneLinkingFn).
require
需要另外一个指令并将其控制器作为第四个参数注入linking函数。需要以指令的字符串名称(或字符串数组)注入。如果使用了数组,注入的参数将是一个数组中的相应的顺序。如果没有找到这样的指令,或如果该指令没有控制器,那么将会出现一个错误。该属性名称前缀为:
无前缀:定位当前元素所需的控制器。如果未找到错误,抛出一个错误。
?:试图找到所需的控制器,如果没有找到,则通过空函数。
^:通过搜索元素的父节点来寻找所需的控制器,如果没找到,抛出个错误。
?^:通过搜索元素的父节点来寻找所需的控制器,如果没有找到,则通过空函数。
controllerAs
指令作用域内的控制器的别名。控制器的别名,以便在该指令模板中引用。该指令需要在适用范围内定义此配置。当指令被用作组件的情况下有用。
restrict
EACM的子集,限制了对一个特定的指令声明式的指令。
E:元素名称,<my-directive></my-directive>
A:元素属性,<div my-directive="exp"></div>
C:元素类:<div class="my-directive: exp;"></div>
M:注释,<!-- directive: my-directive exp -->
template
将HTML的内容的指令块替换当前元素。过程将在新元素上更新之前旧元素的所有属性/类。该属性可以指定模板作为一个字符串或一个函数模板,采用两个参数tElement和tAttrs,并返回一个表示模板的字符串值。
templateUrl
与template基本相同,但模板通过指定的url加载。因为模板是异步加载的,所以conplie和link都会被赞同,等待模板加载完。
replace
指定要插入模块的位置,默认为false。
true:模板将会替换当前元素。
false:模板将会替换当前元素的内容。
transclude
编译元素的内容并且使其在指令内有效。这使得组件有私人的状态,并且嵌入的部分包含到父作用域。
true:该指令可嵌入内容。
false:嵌入整个单元包括在较低的优先级定义的任何指令。
compile
function compile(tElement, tAttrs, transclude) { ... }
compile函数分配模板的转换。由于大多数指令不做模板转换,所以它不经常使用。需要用到compile去转换模板的例子有ngRepeat,或者异步加载内容,如ngView。编译需要如下参数:
tElement:模板元素,该指令已声明的元素。只有在元素和子元素上进行模板转换是安全的。
tAttr:模板属性,在所有的指令compile函数之间共享的属性。
transclude:一个transclude linking函数-- function(scope, cloneLinkingFn)
备注:如果模板被克隆,那么template实例和link实例是不同的对象。因此,所有在compile函数里克隆的DOM节点做DOM转换都是不安全的。具体来说,DOM监听应该在link函数里而不是在compile函数内。
注意:compile函数不能操作指令去递归使用自身的模板或compile函数。编译这些指令将会导致一个无限循环和堆栈溢出错误。可以通过手动使用postLink函数强制编译指令的模板而不是依靠通过template或templateUrl或在compile函数模板手动编译。
link
如果未定义compile属性,则使用此属性。
function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
link函数负责注册DOM监听及更新DOM操作,该函数在模板被克隆后执行。大部分指令逻辑是放在这里面的。
函数参数:
scope:被注册监听的指令使用的作用域。
iElement:元素实例,使用该指令的元素。当子元素已被关联,那么只能在postLink函数内操作是安全的。
iAttrs:属性实例,在这个元素上声明的属性,在所有指令的linking函数内共享。
controller:如果至少有一个指令的元素定义一个控制器,那么是个控制器实例。控制器在所有的指令中被共享,它允许指令使用控制器作为通信通道。
transcludeFn:预绑定到正确的嵌入范围的一个linking函数。范围可以通过一个可选的第一个参数重写--function([scope], cloneLinkingFn)
pre-linking 函数:在关联子元素之前执行。
Post-linking函数:在关联子元素之后执行。
$compile用法:
$compile(element,transclude,maxPriority);
element:将要被编译和插入模板的元素或者HTML字符串。
transclude:指令内有效的函数。Function(angular.Scope,cloneAttachFn=)
maxPriority:只有在指令比给定的优先级低时应用。只影响根元素,不影响子元素。
返回:
一个用于绑定HTML模板到一个作用域的连接函数。
scope:绑定的作用域。
cloneAttachFn=:如果已提供cloneAttachFn=,则连接函数将克隆模板并且调用cloneAttachFn函数允许去在DOM文档中适当的地方附上克隆的元素。
cloneAttachFn被调用如:
cloneAttachFn(clonedElement,scope);
clonedElement:一个被传递到编译器的被克隆的原始元素。
scope:当前scope和linking函数执行的地方。
调用linking函数返回模板的元素。有另一个原始元素将被传递,或者克隆的元素被提供了cloneAttachFn。
在连接视图之后直到调用一次$digest()才会更新,这是Angular自动完成的。
如果你需要获得绑定的页面,有两个方法完成:
1.如果你不想在传给编译器和保持这个参考范围之前去请求克隆模板和创建DOM元素:var element = $compile(‘<p>{{value}}</p>’)(scope);
2.另一方面,你需要元素被克隆,从原始的例子中的视图引用将不指向克隆,而是将要被克隆的模板。在这种情况下,你可以通过cloneattachfn访问克隆:
var templateElement = angular.element(‘<p>{{value}}</p>’); scope = … ;//定义scope var clonedElement = $compile(templateElement)(scope,function(clonedElement,scope){ //在HTML文档的合适位子添加克隆元素 })
使用代码(注意:jQLite的选择器功能有限,此处引入了jQuery):
<div ng-app="Demo" ng-controller="testCtrl as ctrl"> <div id="contianer">a</div> </div>
(function () { angular.module("Demo", []) .service("compileTest", ["$compile",compileTest]) .controller("testCtrl", ["$scope","compileTest",testCtrl]) function compileTest($compile){ this.add = function (s) { var element = angular.element('<span>{{ctrl.words}}</span>'); angular.element("#contianer").append($compile(element)(s)) } }; function testCtrl($scope,compileTest){ var e = angular.element("#contianer"); this.words = "A"; compileTest.add($scope); }; }());
$compile.directive.Attributes
一个包含规范的DOM元素属性并且能在指令的compile和link函数之间共享的对象。这个值反映了当前绑定的状态{{}}。
方法:
$addClass(classVal);
classVal指定元素该添加的css类。如果一个动画样式是可执行的,那么触发该动画样式。
$removeClass(classVal);
classVal指定元素该移除的css类。如果一个动画样式是可执行的,那么触发该动画样式。
$updateClass(newClasses,oldClasses);
更新指定元素的css类。newClasses是新的css类,将要替换oldClasses(已存在样式)。
$observe(key,fn);
观察一个在内部插入的属性。当下一次$digest只会,此观察函数将会被执行一次。当内部插入的值发生变化的时候调用一次此函数。
$set(name,value);
设置DOM元素属性值。name:属性名,value:属性值。
属性:
$attr;
元素属性。
使用代码:
.red{color:red} .blue{color:blue}
<div ng-app="Demo" ng-controller="testCtrl as ctrl"> <div class="blue testClass" data-value="value" new-dir>11111</div> </div>
(function () { angular.module("Demo", []) .directive("newDir",newDir) .controller("testCtrl",angular.noop) function newDir(){ return{ restrict:"ACEM", link:function(scope,element,attrs){ attrs.$addClass("red"); attrs.$removeClass("blue"); attrs.$updateClass("newClass","testClass"); element.bind("mouseover",function(){ attrs.$set("data-text","hello"); }); var count = 0; attrs.$observe("data-text",function(){ console.log(count++); }); } } }; }());
对于指令这块,真的需要多写多用才能真正的理解这些内容及用法... 这篇关于指令的总结文章没多少直接展示的使用代码,不过每个属性都测试过了用法,也写过一些自己项目用的或者自己写着玩的插件... 好了,暂时写到这,接下来去学习另外的服务...