我已经得到了这样的指令:
http://jsfiddle.net/smithkl42/cwrgLd0L/23/
App.directive('prettify', ['$compile', function ($compile) {
var templateFn;
return {
restrict: 'E',
scope: {
target: '='
},
link: function (scope, element, attrs) {
if (!templateFn) {
var template = element.html();
templateFn = $compile(template);
}
scope.$watch('target', function (newVal, oldVal) {
var compiled = templateFn(scope);
element.html('');
element.append(compiled);
var html = element.html();
var prettified = prettyPrintOne(html);
element.html(prettified);
}, true);
}
};
}]);
问题在于,当我编译模板时,它总是使用target
属性的旧值进行编译。所以它开始显示这一点,即它表现得像没有什么可以替代的:
然后,如果我向属性添加一个字符,它会显示这个,即scope.organization.message
属性的 previous 值:
调试显示scope指令的target
属性中的值在编译时是正确的。
我做错了什么?是否有$compile
返回的模板函数查看旧范围值?还是...?
(另请参阅此问题,导致此问题:Using $compile in a directive triggers AngularJS infinite digest error。)
答案 0 :(得分:1)
首先,您的templateFn
变量的范围是工厂级别,但它是在实例级别填充的。这意味着第一次使用该指令时,它将使用该元素的指令进行填充,之后的每个用法也将使用相同的模板,即使它实际上具有不同的模板。
看似延迟绑定问题的原因与摘要周期以及Angular如何管理对DOM的更改有关。在处理范围更改时,范围观察者将在对DOM进行任何更改之前进行处理。这样,所有DOM更改都合并为一个批处理(至少为该周期),因此您不会同时进行多次更新,从而可能导致多次重排。因此,当您调用element.html()
时,您在DOM尚未更新以反映范围内已更改的值时这样做。
在这种特殊情况下,你还要做一些额外的工作 - 调用templateFn
将为你提供一个jQuery(或jQLite)对象,其中包含你需要的内容 - 没有必要将它添加到DOM中,然后将其取回,您可以直接致电html()
。
这个逻辑可以像这样整合(并且正常工作):
setTimeout(function () {
var compiled = templateFn(scope).html();
var prettified = prettyPrintOne(compiled);
element.html(prettified);
}, 0);
在摘要周期结束后,将setTimeout
中的所有内容包装为强制评估逻辑。
但是,一般来说,该指令的实现有点尴尬:
<pre>
和<code>
标签),则应通过template
或{{1}将其包含在指令中}而不是期望消费者知道它是必需的templateUrl
的情况下实现此目的 - 您可以将$compile
的输出放在范围上,然后在prettyPrintOne
中指定的模板中绑定它或template
属性,或者您可以使用jQuery来获取对容器的任何元素的引用(即,如果它不是顶级元素)并使用templateUrl
来设置其内容。 / LI>
html()
选项。