Skip to content Skip to sidebar Skip to footer

Template Always Compiles With Old Scope Value In Directive

I've got a directive that's working like this: http://jsfiddle.net/smithkl42/cwrgLd0L/23/ App.directive('prettify', ['$compile', function ($compile) { var templateFn; retur

Solution 1:

First off, your templateFn variable is scoped at the factory level, but it is populated at the instance level. This means that the first time you use the directive, it will populate using that element's directive, and every usage after that will also use that same template, even if it actually has a different template.

The cause of your seemingly delayed binding issue has to do with the digest cycle and how Angular manages changes to the DOM. When a scope change is being processed, the scope watchers are all processed before any changes are made to the DOM. This way, all DOM changes are consolidated into a single batch (for that cycle at least) so you aren't make multiple updates at once, potentially causing multiple reflows. So, when you're calling element.html(), you're doing so at a point where the DOM hasn't been updated to reflect the changed values on the scope.

In this particular case, you're also doing a bunch of extra work - calling templateFn will give you a jQuery (or jQLite) object with the content you need - there's no need to add it to the DOM, and then take it back out, you can just call html() directly against it.

That logic could all be consolidated (and work correctly) like so:

setTimeout(function () {
    var compiled = templateFn(scope).html();
    var prettified = prettyPrintOne(compiled);
    element.html(prettified);
}, 0);

Wrapping everything in setTimeout forces the logic to be evaluated after the digest cycle is complete.

However, generally speaking, that implementation of the directive is a bit awkward:

  • If there's HTML templating (e.g. <pre> and <code> tags) that is required for each usage, that should be included in the the directive itself via the template or templateUrl properties rather than expecting the consumer to know that it's required
  • You can probably get away implementing this without using $compile - you can either put the output of prettyPrintOne on the scope, and just bind to it in the template specified in the template or templateUrl properties, or you can use jQuery to get a reference to whichever element will be the container (that is, if it's not the top-level element) and use html() to set its content.
  • If you do actually need to allow other templated HTML content to be defined inside the directive, look into the transclude option defined in the directive guide.

Post a Comment for "Template Always Compiles With Old Scope Value In Directive"