在一个项目中,我构建了一个指令 aDir ,在其post-link函数中使用$ compile替换为另一个 bDir 。它为 bDir 创建了一个“快捷方式”,这很有用,因为 bDir 有很多参数,我在整个项目中使用它。
在我的模板中:
<p>
<button a-dir></button>
</p>
编译在:
<p>
<button b-dir arg1="" arg2="" ... ></button>
</p>
使用这样的代码非常有用:
function aDir($compile){
return {
restrict: 'A',
link: function(scope, iElem, iAttrs){
iElem.attr('b-dir', '');
iElem.attr('arg1', '');
iElem.attr('arg2', '');
[...]
iElem.removeAttr('a-dir'); // To avoid infinite loop
$compile(iElem)(scope);
}
}
}
然而,如果应用 aDir 的元素具有子元素,则它们将被编译两次。一旦由Angular发起的$compile
函数和我在{em> aDir 后链接中调用的$compile
函数执行一次。
考虑this plunker。这是HTML:
<outer-level>
<p replace-by-another>
<inner-level>Hello World!</inner-level>
</p>
</outer-level>
replaceByAnother 被名为 another 的指令替换。 outerLevel 和 innerLevel 是指令,只有在调用其编译,前后链接函数时才会登录控制台。
控制台日志是:
outerLevel: compile
replaceByAnother: compile
innerLevel: compile
outerLevel: pre link
replaceByAnother: pre link
innerLevel: pre link
innerLevel: post link
replaceByAnother: post link
another: compile
innerLevel: compile
another: pre link
innerLevel: pre link
innerLevel: post link
another: post link
outerLevel: post link
因此我们有两个 innerLevel 的编译,预链接和后连接函数调用。在我的情况下,这完全可以,但我有兴趣完全理解$compile
做了什么以及是否可以避免这种行为。
我通过在 replaceByAnother 指令中定义编译函数尝试了一些事情,但我只是设法改变执行顺序而不是只编译一次 innerLevel 指令:
http://plnkr.co/edit/ZnBRaskb1WPkRZv36giS
function replaceByAnother($compile){
return {
restrict: 'A',
compile: function(tElem, tAttrs){
console.log('replaceByAnother: compile');
tElem.attr('another', '');
tElem.removeAttr('replace-by-another');
var anotherLinkFunc = $compile(tElem);
return {
pre: function(scope, iElem, iAttrs){
console.log('replaceByAnother: pre link');
},
post: function(scope, iElem, iAttrs){
console.log('replaceByAnother: post link');
anotherLinkFunc(scope);
}
}
}
}
}
结果:
outerLevel: compile
replaceByAnother: compile
another: compile
innerLevel: compile
innerLevel: compile
outerLevel: pre link
replaceByAnother: pre link
innerLevel: pre link
innerLevel: post link
replaceByAnother: post link
another: pre link
innerLevel: pre link
innerLevel: post link
another: post link
outerLevel: post link
你有什么想法吗?
解
通过@georgeawg和@AndrésEsguerra的答案,我找到了一个令人满意的解决方案:
terminal: true
和高优先级来阻止Angular编译两个子节点。$compile
。存储$compile
。function replaceByAnother($compile){
return {
restrict: 'A',
terminal: true,
priority: 100000,
compile: function(tElem, tAttrs){
tElem.attr('another', '');
tElem.removeAttr('replace-by-another');
var anotherLinkFunc = $compile(tElem);
return {
pre: function(scope, iElem, iAttrs){
// pre link
},
post: function(scope, iElem, iAttrs){
anotherLinkFunc(scope, function cloneAttachedFn(clone) {
iElem.replaceWith(clone);
});
}
}
}
}
}
答案 0 :(得分:4)
使用terminal: true
和高优先级。 AngularJS按最高优先级编译指令,因此任何具有更高优先级的指令都将在您之前编译
文档:$compile
function replaceByAnother($compile){
return {
restrict: 'A',
terminal: true,
priority: 1000000,
compile: function(tElem, tAttrs){
console.log('replaceByAnother: compile');
tElem.attr('another', '');
tElem.removeAttr('replace-by-another');
var anotherLinkFunc = $compile(tElem);
return {
pre: function(scope, iElem, iAttrs){
console.log('replaceByAnother: pre link');
},
post: function(scope, iElem, iAttrs){
console.log('replaceByAnother: post link');
anotherLinkFunc(scope);
}
}
}
}
}
答案 1 :(得分:1)
您可以通过克隆tElem
并在编译阶段清空它来避免第一次内部编译。
//replaceByAnother Directive
function replaceByAnother($compile){
return {
restrict: 'A',
compile: function(tElem, tAttrs){
console.log('replaceByAnother: compile');
//clone tElem
var tClone = tElem.clone();
//empty tElem
tElem.empty();
return {
pre: function(scope, iElem, iAttrs){
//console.log('replaceByAnother: pre link');
},
post: function(scope, iElem, iAttrs){
//console.log('replaceByAnother: post link');
//modify and compile cloned elements
tClone.attr('another', '');
tClone.removeAttr('replace-by-another');
var linkFn = $compile(tClone);
linkFn(scope, function transclude(clone) {
iElem.append(clone);
});
}
}
}
}
}
然后在postLink阶段修改和编译克隆元素。