Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting ng-repeat to work inside of AngularJS's $interpolate service

I am using the AngularJs-UI components for Bootstrap. I would like to insert a filled out template into one of the data elements for the popover feature. This works find for all elements not inside of a ng-repeat. How do I get the ng-repeat elements to work inside of a interpolated template?

I have a plunker at http://plnkr.co/edit/Cuku7qaUTL1lxRkafKzv Its not working because I don't know how to get Angular-UI-bootstrap to in plunker.

<div data-popover="{{createHTML()}}">some content</div>

My local scope has the function createHTML() that looks something like this.

angular.module('myApp', ['ngSanitize'])
  .controller("myController", function(myService){
    $scope.createHTML = function() {
      var thingy = { blah: "foobar", collection: [ "1", "2", "3" ] };
      return myService.html_for(thingy);
    }
  });

And the service is

angular.module('myApp')
  .service('myService', function($templateCache, $interpolate, $sanitize, $log) {
    "use strict";

    function html_for(thingy) {
      var template = $templateCache.get('thingyTemplate.html'),
        link = $interpolate(template),
        html = link(thingy),
        unsafe = $sanitize(html);
      return unsafe;
    }

    return {
      html_for: html_for
    }
  });

Templates:

<script type="text/ng-template" id="thingyTemplate.html">
  <div>
    <div><strong>Blah:</strong> {{blah}}</div>
    <div data-ng-repeat="foo in collection"><strong>Collection:</strong> {{foo}}</div>
    <div><strong>Collection[0]:</strong> {{collection[0]}}</div>
    <div><strong>Collection[1]:</strong> {{collection[1]}}</div>
    <div><strong>Collection[2]:</strong> {{collection[2]}}</div>
  </div>
</script>

<script type="text/ng-template" id="template/popover/popover.html">
<div class="popover {{placement}}" data-ng-class="{ in: isOpen(), fade: animation() }">
  <div class="arrow"></div>

  <div class="popover-inner">
    <h3 class="popover-title" data-ng-bind="title" data-ng-show="title"></h3>
    <div class="popover-content" data-ng-bind-html="content"></div>
  </div>
</div>
</script>
like image 272
Jason Avatar asked Jan 03 '14 19:01

Jason


People also ask

Why is NG-repeat not working?

Solution 1. There are two mistakes in your code: In your table, you have to wrap the properties between {{ and }}, for example {{employee. Fname}} instead of just employee.

What is the use of NG-repeat in AngularJS?

AngularJS ng-repeat Directive The ng-repeat directive repeats a set of HTML, a given number of times. The set of HTML will be repeated once per item in a collection. The collection must be an array or an object.

What can I use instead of NG-repeat?

You can consider using transclusion inside a custom directive, to achieve the behavior you are looking for without using ng-repeat.

How do you use two ng-repeat in a table?

NEST TWO ng-repeatThe first ng-repeat in the tr tag will create the rows and the second one in the td tag will create one column per element in the collection. By using one ng-repeat for the row and an other for the column, the rows and the columns of your table is now driven by a subset.


1 Answers

$interpolate doesn't handle directives like ngRepeat ( Difference between parse, interpolate and compile ). $interpolate:

Compiles a string with markup into an interpolation function. This service is used by the HTML $compile service for data binding.

To handle ngRepeat and other directives you want $compile. But for your use case $compile is going to result, unfortunately, in a number of changes because:

  • It needs a scope to compile against rather than just a context like $interpolate. Moreover it needs the scope thingy is on.

    This means we'll need to reference your properties like so {{thingy.blah}} instead of {{blah}} inside your template.

  • The compile needs to happen when the popup is on the dom.

  • The popup is only on the dom when it's open.

So we can't just replace $interpolate with $compile inside your service.

One approach is to replace data-ng-bind-html with the following directive that acts like an ng-bind-html that has a built in $compile (clearly you should only use this with html that you know is safe).

.directive('compile', function($compile) {
  return function(scope, element, attrs) {
    scope.$watch(
      function(scope) {
        return scope.$eval(attrs.compile);
      },
      function(value) {
        var result = element.html(value);
        $compile(element.contents())(scope.$parent.$parent);
      }
    );
  };
});

Used like so (with compile replacing ng-bind-html:

  <div class="popover-content" compile="content"></div>

One issue is that we need thingy to be in scope. There's a few of ways of handling that- but for demonstration purposes I've manually gone back up to the scope the popover is called from - which is 2 scopes up thus the scope.$parent.$parent.

Using this compile directive you no longer $interpolate or $sanitizeso the function in your service can shrink down to just returning the appropriate template:

function html_for() {
  var template = $templateCache.get('thingyTemplate.html');
  return template;
}

demo fiddle

like image 148
KayakDave Avatar answered Sep 23 '22 03:09

KayakDave