4

Here's a jsfiddle showing the problem: http://jsfiddle.net/yRLKe/5/

One button compiles before injecting to the DOM, the other one injects before compiling. I did both to make sure that how I compile wasn't the cause of the problem.

Whichever button you click, check the console output and you'll see that the actual template's contents are not available when selecting from the DOM.

So the question is firstly why? - why does it behave this way? but most importantly how? - how can I get access to the actual loaded template's HTML via DOM selectors?

Here's the template:

<script type="text/ng-template" id="template1.html">
   <div>This is template 1!</div>
   <div id="test">Hello {{name}}</div>
</script>

Here's the controller:

myApp.controller('MyCtrl', function($scope) {
  $scope.name = 'Superhero';
  $scope.template = {url:'template1.html'};
  $scope.clickButton1 = function(){
    $scope.$emit('buttonClicked1');  
  };
  $scope.clickButton2 = function(){
    $scope.$emit('buttonClicked2');  
  };
});

Here's the directive:

myApp.directive('compileBeforeInsert', function($compile){
  return function(scope, element, attrs){
    scope.$on('buttonClicked1',function(ev){
        var container = $('#container');
        container.html('<div ng-include src="template.url" id="template">test1</div>');
        $compile(container)(scope);
        console.log('before');
        console.log($('#template').html());
    });    
  }
});

This directive outputs "test1" to the console whereas I would expect it to output "Hello Superman!".

geoidesic
  • 4,649
  • 3
  • 39
  • 59

2 Answers2

1

The dom is not rendered when the output is written to the console. Using $timeout, you can view the rendered content. MANY people say this is a hack. Regardless, it works. Here's what I changed, with the same result in both directives:

//after injecting $timeout in the directive:
  $compile(container)(scope);
  console.log('before');
  console.log($('#template').children().text());
  $timeout(function(){
      console.log('before, in timeout:');
      console.log($('#template').children().text());
  },0)

Here's the fiddle

Also, see this answer and check out the links within.

Community
  • 1
  • 1
rGil
  • 3,719
  • 1
  • 22
  • 30
  • Ok. Seems to work. However timeout depends on the rendering speed of the browser, so I guess this could be flaky. Hardly seems like a production-ready solution. – geoidesic Jul 07 '13 at 07:46
  • It basically just adds the wrapped objects to the end of the browser queue. So even a delay of 0ms, as in this example works fine. As I said, many people say it's a hack and violates the intent of angular. As @darkporter suggests, there is probably a better solution that does not depend on the actual dom rendering, but rather on the state of the model. Just depends on the real-use need. – rGil Jul 07 '13 at 14:04
0

Your compileBeforeInsert definitely can't work, since you're calling compile -> link -> element but doing nothing with the returned element. It's a no-op.

As to why compileAfterInsert doesn't work, I believe that ng-include is always async, even if the content is already locally available. That's why rgill's setTimeout works.

You might want to re-think this approach... the idea of Angular is that it keeps compiling and digesting over and over until things eventually end up in their stable end state. And there may be asynchronous breaks in that path.

jpsimons
  • 27,382
  • 3
  • 35
  • 45
  • Thx darkporter.. I'm not sure I understand what you are saying though. I also don't think it really answers the important question, which is "how can I get access to the template in the DOM after it is injected?" – geoidesic Jul 07 '13 at 05:35
  • Who or what is rgill? What do you mean when you say, "...you're calling compile->link->element but doing nothing with the returned element"? – geoidesic Jul 07 '13 at 05:47