0

How do you evaluate expressions that are strings in an object that you pass to a directive? I've looked at the following answers, but can't get this to work:

Compiling dynamic HTML strings from database

Dynamically add directive in AngularJS

How to get evaluated attributes inside a custom directive

Cutting to the chase, here is the code:

http://plnkr.co/edit/b2FblYElVGnzZRri34e0?p=preview

It's the {{units}} in the reportData object that I'm trying to evaluate. I've tried using the $compile service, but can't get it work. Thanks in advance!

.js:

var App = angular.module('plunker', [])

.controller("testCtrl", function($scope){
    $scope.units = "Houses";
    mockreport = {"COLUMNS":["People","Units"],"DATA":[[101,"{{units}}"],[100,"test 2"]]};
    $scope.reportData = mockreport;
})
.directive('testdirective', function($compile,$parse,$interpolate){
    return{
      restrict : 'E',
      scope:{  
        units:'@',
        reportData:'='
      },
      templateUrl:'table.html',
      link: function (scope, $el, attrs) {
        curHtml = $el.html();
      recompiledHtml = $compile(curHtml)(scope);
      //$el.html('');
      //$el.append(recompiledHtml);
    }
    };
});

index.html:

 <div data-ng-controller="testCtrl">
   <div class="panel panel-default">

      <testdirective report-data="reportData" units="{{units}}">

      </testdirective>

   </div>
</div>

table.html:

<h4>units: {{units}}</h4>
<table>
  <thead>
     <tr>
        <th data-ng-repeat="header in reportData.COLUMNS" data-ng-class="header">{{header}}</th>
     </tr>
  </thead>
  <tbody>
     <tr data-ng-repeat="column in reportData.DATA">
        <td data-ng-repeat="val in column">{{val}}</td>
     </tr>
  </tbody>
</table>
Community
  • 1
  • 1
jbd
  • 413
  • 5
  • 14

1 Answers1

3

You have the bound property itself having a string which happened to be an angular expression {{units}}. But angular does not know about it, as far as it is concerned it is just another string value to be written to DOM. You may want to make use of the $interpolate service to expand any interpolations in the string and replace it in the string with its value.For example:

interpolate("ABC {{units}}DEF")(scopeOrAnyObject) will return "ABC VALUEOFUNITSDEF"

In your case just as a simplified/minimalist example you could do:

scope.getValue = function(val){
   return angular.isString(val) ? $interpolate(val)(scope) : val;
}

and use it as:

 <td data-ng-repeat="val in column">{{getValue(val)}}</td>

Plnkr

PSL
  • 123,204
  • 21
  • 253
  • 243
  • Ahh. I see. Seems to me then I'd be better off 'fixing' the data at the service level. In this case 'units' is actually an app level value (kind of like like a translation). Since I don't think you can use $interpolate in a service (given that it works with a scope), is there something similar that would work against an arbitrary dataset or even the $rootScope that I can leverage in a service? – jbd Nov 10 '15 at 21:27
  • You can use interpolate without scope, it does not need to be a scope, instead it could be an object itself. Try `$interpolate( "Hello {{unit}}")({unit:'123'})` for yourself. But in your actual case i would not take this up to the controller, instead as you said you do it in service itself. – PSL Nov 10 '15 at 21:29
  • thanks, this put me on a much better path. 1 more quick question if you have the time: in the link function, once you have some html (eg: $el.html() ) isn't it possible to re-compile that html using the $compile service? and if that html has angular expressions in it, shouldn't they resolve? – jbd Nov 10 '15 at 23:21
  • @jbd Yes it will. But you are not compiling the element, but you are compiling the string returned by `.html()`. It has no reference to DOM. Another tricky thing is rendering by ng-repeat which does it in multiple stages, so atleast you have to let the first rendering happen so the ng-repeat outputs the value which is another interpolation. But now if you compile it will double up ng-repeat, for example try out in link function `$timeout(function(){ $compile($el.children())(scope); });` – PSL Nov 11 '15 at 01:38
  • Ahh. It's starting to make sense. So, the $timeout pushes the recompile to the end of the digest cycle at which point $el.html() actually contains the dynamically included {{units}}. But since it still includes the ng-repeat in the mark up, every row is compiled twice. Is that right? Thnks! – jbd Nov 11 '15 at 13:58
  • Also, fwiw, I'm using $interpolate against $rootScope.labels in the data service and it's working great. A better approach. – jbd Nov 11 '15 at 14:07