7

I currently have an underscore.js template that I would also like to use with angular and still be able to use with underscore. I was wondering if it's possible to change the interpolation start and end symbols for a particular scope using a directive, like this:

angular.directive('underscoreTemplate', function ($parse, $compile, $interpolateProvider, $interpolate) {
     return {
         restrict: "E",
         replace: false,
         link: function (scope, element, attrs) {
             $interpolateProvider.startSymbol("<%=").endSymbol("%>");
             var parsedExp = $interpolate(element.html());
             // Then replace element contents with interpolated contents
         }
     }
 })

But this spits out the error

Error: Unknown provider: $interpolateProviderProvider <- $interpolateProvider <- underscoreTemplateDirective

Is $interpolateProvider only available for module configuration? Would a better solution be to simply using string replace to change <%= to {{ and %> to }}?

Also, I noticed that element.html() escapes the < in <%= and > in %>. Is there a way to prevent this automatic escaping?

JD White
  • 807
  • 1
  • 10
  • 15

1 Answers1

4

Ok, you have a couple issues here, but I found a solution for you.

Demo

http://jsfiddle.net/colllin/zxwf2/

Issue 1

Your < and > characters are being converted to &lt; and &gt;, so when you call element.html(), you won't even find an instance of < or > in that string.

Issue 2

Since the $interpolate service has already been "provided" by the $interpolateProvider, it doesn't look like you can edit the startSymbol and endSymbol. However, you can convert your custom startSymbol and endSymbol to the angular start/end symbols dynamically in your linking function.

Solution

myApp.directive('underscoreTemplate', function ($parse, $compile, $interpolate) {
    return {
        restrict: "A",
        link: function(scope, element, attrs) {
            var startSym = $interpolate.startSymbol();
            var endSym = $interpolate.endSymbol();
            var rawExp = element.html();
            var transformedExp = rawExp.replace(/&lt;%=/g, startSym).replace(/&lt;%-/g, startSym).replace(/%&gt;/g, endSym);
            var parsedExp = $interpolate(transformedExp);

            scope.$watch(parsedExp, function(newValue) {
                element.html(newValue);
            });
        }
    }
});

Alternatives

I'm not sure how, but I'm sure there's a way to instantiate your own custom $interpolate service using the $interpolateProvider (after configuring it for underscore tags).

colllin
  • 9,442
  • 9
  • 49
  • 65
  • 1
    Thanks, this works great! I made one small change (use regex with 'g') to replace all instances: http://jsfiddle.net/e3s5d/ – JD White Nov 06 '13 at 21:35
  • Great call. Updated answer. – colllin Nov 07 '13 at 01:19
  • Now that you point out the `<%-` syntax, I'm realizing that the default `<%=` in underscore is more analogous to angular's `
    `. Maybe you could add some logic to this directive to allow unescaped html by replacing `'<%='` in the raw expression with `'
    '`. Reference: http://stackoverflow.com/a/10971756/361609
    – colllin Nov 07 '13 at 01:39