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

Nei*_*kar 6 javascript jquery angularjs angularjs-scope angular-directive

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

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

我在这做错了什么?

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

的index.html

<div ng-app="app">
    <debug source='html'></debug>
    <div id="target"></div>
</div>
Run Code Online (Sandbox Code Playgroud)

的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)
Run Code Online (Sandbox Code Playgroud)

产量

scope 003 loaded from html
scope 005 loaded from xhr (auto-applied)
scope {{$id}} loaded from {{source}}
Run Code Online (Sandbox Code Playgroud)

更新:解决方案适用于具有模板属性的指令,但不适用于templateUrl

所以,我本来应该编译dom节点,而不是HTML字符串.但是,如果指令包含templateUrl,则此更新的小提示显示相同的失败行为:

http://jsfiddle.net/trz80n9y/3/

Gre*_*egL 22

您可能已经意识到,您需要调用$scope.$apply()它来更新{{bindings}}范围值.

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

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

代替:

target.innerHTML = $compile(html)($scope)[0].outerHTML
$scope.$apply()
Run Code Online (Sandbox Code Playgroud)

将其更改为:

target.innerHTML = html;
$compile(target)($scope);
$scope.$digest();
Run Code Online (Sandbox Code Playgroud)

请注意,我做了一个$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()
Run Code Online (Sandbox Code Playgroud)

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

  • 是的,这完全有道理。出于某种原因,我认为 angular 会编译分离的 dom 节点,尽管现在我大声说我们只是在谈论一个字符串,而不是 DOM 节点。感谢您对 $digest 与 $apply 的简洁描述:) (2认同)