0

I want to render a list of widgets on a page from my controller. Each widget has its own render function that returns a safe HTML string.

My first part of my ng-repeat (outside layer) looks like this:

$scope.renderWidgets = function() {
  var html = "";
  html += "<div ng-repeat='widget in widgets'>";
  html += "<div ng-bind-html='widget.render()'></div>"
  html += "</div>";
  return $sce.trustAsHtml(html);
}

As you can see I have ng-bind-html inside my ng-repeat which is calling a function named .render() located inside widget:

this.render = function() {
  var html = "";
  html += "<div ng-repeat='choice in widget.choices'>";
  html += "{{choice.name}}";
  html += "</div>";
  return $sce.trustAsHtml(html);
}

I also have a directive that I use to $compile the above HTML string into AngularJS.

If I run the above code the widget.render() function gets called just fine but the output on the page will look like this: {{choice.name}} and not the value inside choice.name.

As you can see I am trying to do another ng-repeat inside ng-bind-html with the widget object from my first ng-repeat.

Is this even possible what I am trying to do here (I am new to AngularJS)? If yes then what am I doing wrong? Or is there another way to resolve my problem?

I am using the latest AngularJS (v. 1.7.9).

UPDATE

I think I know the issue. When I call my first function $scope.renderWidgets() the returned HTML will be automatically compiled(the first ng-repeat) at which point the ng-bind-html gets triggered returning a non-compiled HTML string. Now I have to figure out a way to $compile the returned HTML from my ng-bind-html directive.

Am I on the right track?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Asp1re
  • 353
  • 1
  • 12

1 Answers1

0

The ng-bind-html directive only binds HTML. It does not compile HTML. This was a deliberate decision by the AngularJS team to avoid security problems.

You state that you have a directive that compiles HTML. Let's assume the directive is:1

app.directive('dynamic', function ($compile) {
  return {
    restrict: 'A',
    replace: true,
    link: function (scope, ele, attrs) {
      scope.$watch(attrs.dynamic, function(html) {
        ele.html(html);
        $compile(ele.contents())(scope);
      });
    }
  };
});

Then use it in your nested code:

$scope.renderWidgets = function() {
  var html = "";
  html += "<div ng-repeat='widget in widgets'>";
  ̶h̶t̶m̶l̶ ̶+̶=̶ ̶"̶<̶d̶i̶v̶ ̶n̶g̶-̶b̶i̶n̶d̶-̶h̶t̶m̶l̶=̶'̶w̶i̶d̶g̶e̶t̶.̶r̶e̶n̶d̶e̶r̶(̶)̶'̶>̶<̶/̶d̶i̶v̶>̶"̶
  html += "<div dynamic='widget.render()'></div>"
  html += "</div>";
  return $sce.trustAsHtml(html);
}

For more information, see

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • Thank you, works like a charm. I would also note for other people that I had to remove `$sce.trustAsHtml()` from my second `ng-repeat` to avoid an infinite digest loop error. – Asp1re Jan 15 '20 at 04:56