angularJs关于指令的一些冷门属性详解
我们使用ng的时候,经常会使用到指令,大家所熟知的属性我在这里就不介绍了,讲讲大家没怎么留意的属性
1.multielement
这是指定指令作用区间的功能,最常用的就是ng-repeat-start和ng-repeat-end了。
2.priority
指令优先级,优先级越高,指令越早执行。
3.terminal
是否允许优先级低的指令起作用,如果是true,那么只有比当前指令或跟当前指令等级相同的指令才可以执行。最典型的就是ngif
4.templatenamespace
声明模板的格式有三种选择 svg、html、math
5.transclude
或许有人疑问了,transclude也算是冷门属性吗?其实大家对transclude了解并没有想象的那么深,transclude是一个挺复杂的属性,一般大家会用到的也仅仅是true,false。这两个属性我在这里就不讲了,在这里我主要讲的是transclude:element,我google了一整天都没找到正确描述这个属性的方法。我觉得google出来的答案太文档化了。最后在研究$transclude才看出来这个属性的功能究竟在哪里。再讲功能前我们先了解下$transclude
无论在指令的compile还是link时期我们的最后一个参数就是$transclude了,这里其实我们看看源码是如何定义的,我看的源码是ng1.5.3的
function controllersboundtransclude(scope, cloneattachfn, futureparentelement, slotname) { var transcludecontrollers; // no scope passed in: if (!isscope(scope)) { slotname = futureparentelement; futureparentelement = cloneattachfn; cloneattachfn = scope; scope = undefined; } if (haselementtranscludedirective) { transcludecontrollers = elementcontrollers; } if (!futureparentelement) { futureparentelement = haselementtranscludedirective ? $element.parent() : $element; } if (slotname) { // slottranscludefn can be one of three things: // * a transclude function - a filled slot // * `null` - an optional slot that was not filled // * `undefined` - a slot that was not declared (i.e. invalid) var slottranscludefn = boundtranscludefn.$$slots[slotname]; if (slottranscludefn) { return slottranscludefn(scope, cloneattachfn, transcludecontrollers, futureparentelement, scopetochild); } else if (isundefined(slottranscludefn)) { throw $compileminerr('noslot', 'no parent directive that requires a transclusion with slot name "{0}". ' + 'element: {1}', slotname, startingtag($element)); } } else { return boundtranscludefn(scope, cloneattachfn, transcludecontrollers, futureparentelement, scopetochild); } }
还有一个另一个函数要特别指出来,就是最后返回的 boundtranscludefn 这个方法,下面是他的源码
function createboundtranscludefn(scope, transcludefn, previousboundtranscludefn) { function boundtranscludefn(transcludedscope, clonefn, controllers, futureparentelement, containingscope) { if (!transcludedscope) { transcludedscope = scope.$new(false, containingscope); transcludedscope.$$transcluded = true; } return transcludefn(transcludedscope, clonefn, { parentboundtranscludefn: previousboundtranscludefn, transcludecontrollers: controllers, futureparentelement: futureparentelement }); }
这两个方法到底是在做什么呢?其实就是克隆了当前指令的节点,并生成子作用域。克隆的节点由transclude定义,如果你的属性是true,则克隆的是指令模板中的ng-transclude所在的dom节点,及其子节点。如果属性是element则克隆整个模板的节点。
这是两个指令的代码
angular.module('myapp', []) .directive('droppanel', function() { return { transclude: 'element', replace: true, template: "<div class='drop-panel'>" + "<span ng-transclude class='111'></span>" + "</div>", link: function(scope, el, c, d, $transclude) { $transclude(function ngrepeattransclude(clone, scope) { console.log(clone); }) } } }) .directive('droppanel2', function() { return { transclude: true, replace: true, template: "<div class='drop-panel'>" + "<span ng-transclude class='111'></span>" + "</div>", link: function(scope, el, c, d, $transclude) { $transclude(function ngrepeattransclude(clone, scope) { console.log(clone); }) } } })
如果你觉得replace干扰了对结果的理解,你可以注释掉,然后查看控制台中打印出来的clone,你就能知道所谓transclude的属性声明为element的作用了,我们打开replace目的在于能较清楚的查看dom节点,来获得结论,下面就是两者编译后dom节点的区别了
看完上面的图,你可以明显的区别到两者对dom的克隆不一样的,另外如果在声明属性为‘element'时,需要声明replace为true,才能渲染出来。我查了很多资料,最终用断点得出了我认为对的结论,断点追踪的结果是发现如果不声明replace,好像就不会执行ngtransclude指令,这点我很奇怪,正因为这样子所以导致没有成功渲染。二归根结底其实是两者的操作的dom元素不同,在声明transclude为element时,replace为true,你取到的dom节点是含有transclude属性的节点(子节点),而为false你拿到的并不是含有transclude属性的节点(父节点),而ng本身不对其节点进行遍历,导致没能执行ngtransclude指令
我看到一个观点觉得不错,大概意思就是:源于功能的考虑,在使用element属性的时候,一般都是起占位符的作用,你需要做的操作是对dom的添加时候,才会用到这个克隆功能。
我觉得这个观点不错,看过很多关于ngrepeat的介绍,很多文章都说ngrepeat源码是通过$scope.$new()来生成子作用域的,实际上并不完全正确,他的确是通过$scope.$new产生子作用域的,但是这个产生功能是交给$transclude函数去做得,实际上ngrepeat的源码上是通过$transclude来生成子作用域和添加dom节点的。与上面的观点有相似之处。
以上就是小编为大家带来的angularjs关于指令的一些冷门属性详解全部内容了,希望大家多多支持~
上一篇: 马桶里漂着个碗
下一篇: Vue实例简单方法介绍
推荐阅读