Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS: Watch transcluded content change with interpolation?

I've put together a plunker that best illustrates my issue.

I'm attempting to write a simple directive that will update the transcluded html from a directive.

For example I would like to have:

<make-bold>Foo *bar* {{something}}</make-bold> 

Generate into

<span>Foo <b>bar</b> somevalue<span>  

The example on plunker works fine, but I can't figure out how to get notifications (watch) of the transcluded content changing. In the example, try selecting different items (by clicking on them), and you'll notice "Processed" does not update.

I'm pretty sure the issue is that the element passed to the link function does not update, but it's content updates, thus it can't be watched.

Directive

app.directive('makeBold', function($interpolate, $timeout, $compile)
{

  var regex = new RegExp("\\*(.+?)\\*", 'g');
  var replace = "<b>$1</b>";

  return {
    restrict: 'E',
    transclude: true,
    replace: true,
    template: '<span ng-transclude></span>',
    link: function (scope, element, attrs)
    {
      scope.$watch(
        function() { 
          // *** What do I need to watch here? ***
          return element;
        }, 
        function(n, o) {
          var text = $interpolate(element.text())(scope); 
          var newContent = text.replace(regex, replace);
          $timeout(function() { element.html(newContent); });
      });

    }
  };
});

Template

<div ng-show="selected">
    <h1>{{selected.name}}</h1>
    <p><i>Original:</i> {{selected.detail}}</p>
    <p><i>Processed:</i> <make-bold>{{selected.detail}}</make-bold></p>
</div>

(Note: I don't actually want to make a 'make-bold' directive, but this illustrates the issues I am having.)

like image 364
Phil Price Avatar asked Jan 09 '14 01:01

Phil Price


1 Answers

here is a working plunker

This one was not obvious and very challenging. I had to look inside angular.js source code to fully understand where transcluded contents live and what angular doing with interpolated text.

The trick here is to disable angular interpolation and use $interpolate manually.

addTextInterpolateDirective function inside compile.js is responsible for the magic binding of interpolation ( {{ }} ).

Later, I will update this answer with detailed explanation.

Solution:

app.directive('makeBold', function( $interpolate ) {

  var regex = new RegExp("\\*(.+?)\\*", 'g');
  var replace = "<b>$1</b>";

  return {
    restrict: 'E',
    scope: true,
    compile: function (tElem, tAttrs) {
      var interpolateFn = $interpolate(tElem.html(), true);
      tElem.empty(); // disable automatic intepolation bindings
      return function(scope, elem, attrs){
        scope.$watch(interpolateFn, function (value) {
          var newContent = value.replace(regex, replace);
          elem.html(newContent);
        });
      }
    }
  };
});
like image 71
Ilan Frumer Avatar answered Sep 25 '22 17:09

Ilan Frumer