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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27008280/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 06:49:21  来源:igfitidea点击:

AngularJS: Using $compile on html that contains directives with templateurl

javascriptjqueryangularjsangularjs-scopeangular-directive

提问by Neil Sarkar

I have a legacy application that has some content inserted into the DOM via jQuery. I would like the legacy parts of the codebase to be responsible for compiling the html that it inserts into the DOM.

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

I can get it to compile the initial html using $compile, but any DOM elements added by a directive's template or templateUrl are not compiled, unless I call $scope.$apply()from within the directive itself.

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

What am I doing wrong here?

我在这里做错了什么?

Link to fiddle: http://jsfiddle.net/f3dkp291/15/

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

index.html

索引.html

<div ng-app="app">
    <debug source='html'></debug>
    <div id="target"></div>
</div>

application.js

应用程序.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)

output

输出

scope 003 loaded from html
scope 005 loaded from xhr (auto-applied)
scope {{$id}} loaded from {{source}}


Update: Solution works for directives with a template property, but not templateUrl

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

So, I should have been compiling dom nodes, not an HTML string. However, this updated fiddle shows the same failing behavior if the directive contains a templateUrl:

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

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

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

回答by GregL

As you probably realised, you need to call $scope.$apply()for it to update the {{bindings}}from the scope values.

正如您可能意识到的那样,您需要调用$scope.$apply()它来更新{{bindings}}范围值。

But the reason you couldn't do it inside your async function was that you were compiling the HTML against the existing scope for #target, but then trying to append just the HTML. That won't work, because you need to have the compiled node in the DOM, either by appending the entire compiled node using jQuery's .append()or similar, or by setting the DOM innerHTMLfirst, then compiling the node that is in the DOM. After that, you can call $applyon that scope and because the directive is compiled and in the DOM, it will be updated correctly.

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

In other words, change your async code as follows.

换句话说,按如下方式更改异步代码。

Instead of:

代替:

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

Change it to:

将其更改为:

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

Note that I did a $digest()instead of $apply(). This is because $apply()does a digest of every single scope, starting from the $rootScope. You only need to digest that one scope you linked against, so it is sufficient (and faster, for any reasonably sized app with lots of scopes) to just digest that one.

请注意,我做了一个$digest()而不是$apply(). 这是因为$apply()$rootScope. 您只需要消化您链接的那个作用域,因此只需消化那个作用域就足够了(而且速度更快,对于任何具有大量作用域的合理大小的应用程序)。

Forked fiddle

分叉的小提琴

Update: Angular can compile strings and detached DOM nodes

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

I just checked, and the OP was actually correct in assuming that Angular can compile strings of HTML or detached DOM nodes just fine. But what you do need to do is make sure you actually append the compiled nodeto the DOM, not just the HTML. This is because Angular stores things like the scope and the binding information as jQuery/jQueryLite data on the DOM node*. Thus you need to append the whole node, with that extra information, so that the $digest()will work.

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

So an alternative way of having this work is to change the same portion of the OP's code as above to:

因此,进行这项工作的另一种方法是将 OP 代码的相同部分更改为:

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

* Technically, it is stored in the internal jQuery data cache, with the cache key being stored on the DOM node itself.

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

回答by Enzey

Append the element to the target first, then compile it.

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

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

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

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