AngularJS: using $ compilation in html containing template directives

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

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

What am I doing wrong here?

Link to the violin: http://jsfiddle.net/f3dkp291/15/ p>

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 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: the solution works for directives with the template property, but not with the Url template

So, I had to compile dom nodes, not an HTML string. However, this updated script shows the same failure behavior if the directive contains templateUrl:

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

+6
source share
2 answers

As you probably understood, you need to call $scope.$apply() so that it updates {{bindings}} from the scope values.

But the reason you couldn't do this inside your asynchronous function was because you compiled HTML against the existing scope for #target , but then tried to add only HTML. This will not work, because you need to compile the node in the DOM by adding the entire compiled node using jQuery .append() or similar, or first setting the DOM innerHTML and then compiling the node that is in the DOM. After that, you can call $apply in this area and, since the directive is compiled in the DOM as well, it will be updated correctly.

In other words, change your asynchronous code as follows.

Instead:

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

Change it to:

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

Note that instead of $apply() I did $digest() . This is because $apply() digests every single area, starting with $rootScope . You only need to digest one area with which you are connected, so it’s enough (and faster for any application with a reasonable size with a lot of areas) to simply digest it.

Corked violin

Update: Angular can compile strings and individual DOM nodes

I just checked and the OP was actually right, suggesting that Angular could compile strings of HTML or individual DOM nodes just fine. But what you need to do is make sure that you are really adding the compiled node to the DOM, not just the HTML. This is because Angular stores things like scope and binding information like jQuery / jQueryLite data on a DOM node * . So you need to add a whole node, with this additional information, for $digest() work.

Thus, an alternative way of doing this work is to change the same part of the OP code as above to:

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

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

+20
source

Add the item 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/

+2
source

Source: https://habr.com/ru/post/978367/


All Articles