1

Why does the link evaluate the expression inside the directive using the link attribute, but the template does not? Note that I'm only using 'link' here for console.log illustrative purposes.

My end goal is to have data passed into a directive via its attribute and have that data render as its template.

index.html

<!DOCTYPE html>
<html>

<head>
  <script data-require="angular.js@*" data-semver="1.3.7" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.7/angular.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="app.js"></script>
</head>

<body ng-app="myApp">
  <div ng-controller="myCtrl">
      <my-directive text="{{data}}" />
  </div>
</body>

</html>

app.js

angular.module("myApp", []).directive("myDirective", function () {
  return {
      restrict: "E",
      scope: {
          text: "@text",
      },
      link: function(scope, element, attrs){
        console.log(attrs.text);
      },
      template: function(element, attrs){
        console.log(attrs.text);
      }
  };
}).controller('myCtrl',function($scope){
$scope.data = 'test';
});

output:

{{data}}
test
2
  • So are you trying to use the text attribute to specify what the contents of the directive are? Commented Jan 5, 2015 at 2:28
  • @GregL Yes, if by "contents of the directive" you mean a template. I want to be able to directly use in my template what is passed in through the html attribute. Commented Jan 5, 2015 at 2:35

3 Answers 3

2

I recommend GregL's answer, but for completeness:

By its nature, the template code has to get run before angular compiles your code (obviously, since the template is telling angular what it should compile). This is why the result is different than what you see in the link function, which happens after compilation.

If you absolutely must perform manual interpolation, you can use the $interpolate service angular provides.

https://docs.angularjs.org/api/ng/service/$interpolate

Sign up to request clarification or add additional context in comments.

2 Comments

I can't see how you would use $interpolate inside your template function, because you don't have access to the scope at that point, and $interpolate gives you a function which takes in a context, such as a scope.
Whoops, good call Greg! Yes, the template function doesn't have access to $scope, so you have to fake it using something like onehungrymind.com/angularjs-dynamic-templates . Definitely try transclusion before that, because you end up having to manually redo a lot of what angular normally gives you automatically.
1

So I believe that you are confusing the two scopes you are dealing with here. By using an isolate scope like you have here, you are copying the interpolated value of the text attribute (evaluated against the scope at the point the directive is encountered (the controller scope in your example)) to a property on the isolate scope called 'text'. So within your directive's template, you should be referring to text to access that value ("test" in your case).

However, based on your comment, you really do want to be able to pass in the markup for the directive to display, as if it were running against the controller's scope. This is what transclusion is for - the transcluded content will be placed where you indicate inside the directive template and evaluated against the controller's scope, so {{data}} will correctly resolve to test.

In that case, your markup would become:

<my-directive>{{data}}</my-directive>

And your JS becomes:

angular.module("myApp", []).directive("myDirective", function() {
  return {
    restrict: "E",
    scope: {
      // no need for scope bindings here
    },
    transclude: true,
    link: function(scope, element, attrs) {
      // nothing to do here for now
    },
    template: '<div ng-transclude></div>',
  };
}).controller('myCtrl', function($scope) {
  $scope.data = 'test';
});

Working Plunker

2 Comments

Actually Greg, the first thing you described sounds more similar to what I was trying to do! I want to take data from the controller accessible from the point where the directive is used, pass that data in through the directives attribute, use that data inside the directive, and return a template that uses the passed in data in a trivial manner (wrapped in an <h3> or <h2> or something). Do you think I should still be pursuing transclusion as an implementation? My example was set up to try to understand why it evaluates inside the Link function but not Template function.
If it is super simple, where the directive has no logic of its own, it is just a reusable way to wrap some text in standard markup, then just remove the scope parameter and then return '<h3>' + attrs.text + '</h3>'; or similar in the template function. Or stick to using transclusion and a simple template like: '<h3 ng-transclude></h3>'. Both should be fairly equivalent.
0

One way of accessing controller scope from your directive is make the

scope: false

read more about directives here

HTH

2 Comments

Shouldn't I just be able to copy the controller data into the directive's isolated scope and access it throughout my directive via 'text: @text'? My goal is not necessarily to access the controller's scope.
You can bind u r model like scope: { ngModel: '=' }, , read more in scope section in this article ng-newsletter.com/posts/directives.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.