我有一个遗留应用程序,该应用程序通过jQuery将一些内容插入了DOM。我希望代码库的遗留部分负责编译它插入DOM中的html。
我可以使用来编译初始html $compile,但是除非我$scope.$apply()从指令内部调用,否则不会编译指令模板或templateUrl添加的任何DOM元素。
$compile
$scope.$apply()
我在这里做错了什么?
链接到小提琴: http://jsfiddle.net/f3dkp291/15/
index.html
<div ng-app="app"> <debug source='html'></debug> <div id="target"></div> </div>
application.js
angular.module('app', []).directive('debug', function() { return { restrict: 'E', template: "scope {{$id}} loaded from {{source}}", link: function($scope, el, attrs) { $scope.source = attrs.source if( attrs.autoApply ) { // this works $scope.$apply() } }, scope: true } }) // mimic an xhr request setTimeout(function() { var html = "<div><debug source='xhr (auto-applied)' auto-apply='1'></debug><br /><debug source='xhr'></debug></div>", target = document.getElementById('target'), $injector = angular.injector(['ng','app']), $compile = $injector.get('$compile'), $rootScope = $injector.get('$rootScope'), $scope = angular.element(target).scope(); target.innerHTML = $compile(html)($scope)[0].outerHTML // these do nothing, and I want to compile the directive's template from here. $scope.$apply() $scope.$root.$apply() angular.injector(['ng','app']).get('$rootScope').$apply() }, 0)
输出
scope 003 loaded from html scope 005 loaded from xhr (auto-applied) scope {{$id}} loaded from {{source}}
更新:解决方案适用于具有模板属性的指令,但不适用于templateUrl
因此,我应该一直在编译dom节点,而不是HTML字符串。但是,如果指令包含templateUrl,则此更新的小提琴将显示相同的失败行为:
http://jsfiddle.net/trz80n9y/3/
正如您可能意识到的那样,您需要调用$scope.$apply()它以{{bindings}}从范围值更新。
{{bindings}}
但是您无法在异步函数中执行此操作的原因是,您正在针对的现有范围编译HTML #target,然后尝试仅追加HTML。那是行不通的,因为您需要在DOM中拥有已编译的节点,要么通过使用jQuery .append()或类似方法附加整个已编译的节点,要么innerHTML先设置DOM ,然后再编译DOM中的节点。之后,您可以调用$apply该作用域,并且由于该指令是在DOM中编译的,因此它将正确更新。
#target
.append()
innerHTML
$apply
换句话说,如下更改您的异步代码。
代替:
target.innerHTML = $compile(html)($scope)[0].outerHTML $scope.$apply()
更改为:
target.innerHTML = html; $compile(target)($scope); $scope.$digest();
请注意,我做了一个$digest()代替$apply()。这是因为$apply()从开始对每个范围进行了摘要$rootScope。您只需要消化与之链接的那个作用域,因此就足以消化该作用域(对于具有许多作用域的任何大小合理的应用程序,速度都更快)。
$digest()
$apply()
$rootScope
分叉的小提琴
我刚刚检查了一下,OP实际上是正确的,假设Angular可以很好地编译HTML字符串或分离的DOM节点。但是,您需要做的是确保将编译后的 节点 实际附加到DOM,而不仅仅是HTML。这是因为Angular将诸如范围和绑定信息之类的内容存储为DOM节点*上的jQuery / jQueryLite数据。因此,您需要在整个节点上附加这些额外的信息,以便$digest()可以工作。
因此,进行此工作的另一种方法是将上述OP的代码的相同部分更改为:
target.appendChild($compile(html)($scope)[0]); $scope.$digest()
*从技术上讲,它存储在内部jQuery数据高速缓存中,而高速缓存键存储在DOM节点本身上。