Snixtor Snixtor - 5 months ago 30
AngularJS Question

Provide template with expressions as attribute on directive

I'm wanting to pass a "template" into a directive, by means of an attribute. Here's a trite example of what I'm trying to accomplish:

This HTML:

<greeter person-name="Jim" greeting-template="Hello {{name}}"></greeter>


Would produce output:
Hello Jim
.

I've tried with a directive like this:

function greeter($interpolate) {
var directive = {
link: link,
restrict: 'EA',
template: '<div>{{evaluatedTemplate}}</div>'
};
return directive;

function link(scope, element, attrs) {
scope.name = attrs.personName;
scope.evaluatedTemplate = $interpolate(attrs.greetingTemplate)(scope);
}
}


But that doesn't work, because
{{name}}
in the
greeting-template
attribute gets evaluated in the parent scope before it gets as far as the directive link function.

Ultimately, I would need the value of
attrs.greetingTemplate
to literally be a string of: 'Hello {{name}}'. I figure I could do it with some alternative syntax, like having the
greeting-template
attribute value as: "Hello [name]" and convert "[" to "{{" before interpolation. But that feels messy. I looked at transclusion too, but the way it evaluates the directive against the parent scope looks like it could cause issues when I have multiple greeter's.

Answer

Instead of using the link function, you could use the compile function, which runs before any linking to a scope occurs, and gets passed the template element (the original DOM element) as well as its uninterpolated attributes as arguments. I think that's what you're looking for here.

In the compile function, you could store the uninterpolated template string in a variable for later use in your post-link function (which is the same as the link function if you use link rather than compile), where you can then bind it to your scope.

So your directive would look like this, with a compile property rather than a link property:

function greeter($interpolate) {
  var directive = {
    compile: compile,
    restrict: 'EA',
    scope: true,
    template: '<div>{{evaluatedTemplate}}</div>'
  };
  return directive;

  function compile(tElement, tAttrs) {

    // save the uninterpolated template for use in our post-link function
    var greetingTemplateUninterpolated = tAttrs.greetingTemplate;

    return {
      pre: function (scope, element, attrs) {},
      post: function (scope, element, attrs) {
        scope.name = attrs.personName;
        scope.evaluatedTemplate = $interpolate(greetingTemplateUninterpolated)(scope);
      }
    };
  }  
}

Here's a fiddle showing it working.

And here's a really good article explaining how compile and link work.

Comments