AngularJS:在html上使用$ compile,其中包含带有templateurl的指令

我有一个遗留应用程序,它通过jQuery将一些内容插入到DOM中。 我希望代码库的遗留部分负责编译它插入DOM的html。

我可以使用$compile来编译初始html,但是不会编译由指令的模板或templateUrl添加的任何DOM元素,除非我从指令本身调用$scope.$apply()

我在这做错了什么?

链接到小提琴: http : //jsfiddle.net/f3dkp291/15/

的index.html

 

的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 = "

", 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}}

但是你无法在异步函数中执行此操作的原因是您正在针对#target的现有范围编译HTML,但之后尝试仅添加HTML。 这是行不通的,因为您需要在DOM中使用已编译的节点,方法是使用jQuery的.append()或类似方法附加整个编译节点,或者先设置DOM innerHTML ,然后编译其中的节点。 DOM。 之后,您可以在该范围上调用$apply ,因为该指令已编译,并且在DOM中,它将被正确更新。

换句话说,更改您的异步代码如下。

代替:

 target.innerHTML = $compile(html)($scope)[0].outerHTML $scope.$apply() 

将其更改为:

 target.innerHTML = html; $compile(target)($scope); $scope.$digest(); 

请注意,我做了$digest()而不是$apply() 。 这是因为$apply()$rootScope开始对每个范围进行摘要。 您只需要消化您链接的一个范围,因此只需消化那个范围就足够了(并且对于任何具有大量范围的合理大小的应用程序来说更快)。

叉小提琴

更新:Angular可以编译字符串和分离的DOM节点

我刚刚检查过,OP实际上是正确的,假设Angular可以编译HTML或分离的DOM节点的字符串就好了。 但您需要做的是确保实际将已编译的节点附加到DOM,而不仅仅是HTML。 这是因为Angular将范围和绑定信息等内容存储为DOM节点*上的jQuery / jQueryLite数据。 因此,您需要使用该额外信息附加整个节点,以便$digest()可以工作。

因此,另一种方法是将上述OP代码的相同部分更改为:

 target.appendChild($compile(html)($scope)[0]); $scope.$digest() 

*从技术上讲,它存储在内部jQuery数据缓存中,缓存密钥存储在DOM节点本身上。

首先将元素附加到目标,然后编译它。

 html = angular.element(html); target = angular.element(target); target.append(html); html = $compile(html)($scope) 

http://jsfiddle.net/f3dkp291/16/