1

I have an array with multiple objects, similar to this:

[ 
  { title: 'abc', 'pre': '<div class="class1"><div class="class2">', 'post': '</div>' },
  { title: 'def', 'pre': <div class="class3">', 'post': '</div>' },
  { title: 'ghi', 'pre': '<div class="class3">', 'post': '</div></div>' }
]

<div ng-repeat="item in myVar">
  <div ng-bind-html="item.pre" />{{ item.title }}<div ng-bind-html="item.post" />
</div>

The above does not work (I have to open two div's in one, and close in two other items in that array, as illustrated above). The problem is that ng-bind-html needs to be bound to an element, which I cannot use, neither does a filter work:

<div ng-repeat="item in myVar">
  {{ item.pre | trust }}{{ item.title }}{{ item.post | trust }}
</div>

angular.module('myModule').filter('trust', ['$sce',function($sce) {
  return function(value, type) { return $sce.trustAsHtml; }
}]);

Any ideas?

chrney
  • 257
  • 1
  • 2
  • 12

1 Answers1

1

You'll have to perform the concatenation pre-view, trust that (or turn on ngSanitize, potentially better-yet), then inject it.

As far as I know, there's no way to inject a partial HTML element the way you're trying to.

In your controller:

$scope.items = [...];

for (var i = 0; i < $scope.items.length; i++) {
    var e = $scope.items[i];

    e.concatenated = $sce.trustAsHtml(e.pre + e.title + e.post);
}

Then in your view:

<div ng-repeat="item in items">
    <div ng-bind-html="item.concatenated" />
</div>

Of course, you'll probably want ngSanitize turned on, just to avoid any issues with e.title. That is, if someone entered a title of <script>alert('ahh!')</script>, that would end up being trusted.


Your version did not work because of how ngBindHtml is written:

var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
  return {
    restrict: 'A',
    compile: function ngBindHtmlCompile(tElement, tAttrs) {
      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
        return (value || '').toString();
      });
      $compile.$$addBindingClass(tElement);

      return function ngBindHtmlLink(scope, element, attr) {
        $compile.$$addBindingInfo(element, attr.ngBindHtml);

        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
          // we re-evaluate the expr because we want a TrustedValueHolderType
          // for $sce, not a string
          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
        });
      };
    }
  };
}];

It injects using element.html(...), which needs a complete HTML element.

Matthew Haugen
  • 12,916
  • 5
  • 38
  • 54
  • I use ng-sanitize, yes. Is the concatination really the only way? – chrney May 02 '16 at 19:18
  • @chrney Yes, as far as I know. See my edit for a slight bit of why. There *might* be a workaround out there, but honestly I'd be tempted to stick with concatenation. You could write a directive to do that concatenation, if need be. – Matthew Haugen May 02 '16 at 21:39