AngularJS ng-repeat applied multiple times in $compiled directive

邮差的信 提交于 2019-12-12 21:27:09

问题


I've written a directive that dynamically creates a popover for an element:

app.directive('popover', function($compile, $timeout){
    return {    
        link: function(scope, element, attrs) {

            $timeout(function() {

                // grab template
                var tpl = $(element).find('.popover-template')

                // grab popover parts of template
                var template = {
                    //$compile( $(element).siblings(".pop-content").contents() )(scope)
                    title: tpl.find('.template-title').contents(),
                    content: tpl.find('.template-content').contents()
                };

                // render template with angular
                var content = $compile(template.content)(scope);
                var title = $compile(template.title)(scope); 

                $(element).popover({
                    html: true,
                    placement: "right",
                    content: content,
                    title: title
                });

                scope.$digest()
            });

        }

    };
});

In application it looks like this:

<span popover>Click me</span>
<div ng-hide="true" class="popover-template">
    <div class="template-title">
        <strong>{{ x.name }} and {{ y.name }}</strong>
    </div>

    <div class="template-content">
        <div>
            <pre>f in [1,2,3]</pre>
            <div ng-repeat="f in [1,2,3]">
                item {{ f }}, index {{ $index }}
            </div>
        </div>
    </div>

</div>

The popover is created and displayed. The title works correctly as well. However, ng-repeat is applied multiple times in any iteration:

As you can see, the iteration that should only include 3 elements in fact includes 3*3 elements. The directive creates popovers for exactly 3 elements, so I guess that's where my mistake lies. How can I make sure that within each popover, ng-repeat is only called once?


回答1:


The problem

Since the popover-template element is already in the document when you bootstrapped the angular application (at page load), it has already been compiled once. The ng-repeat element is replaced with 3 new elements:

<!-- original -->
<div ng-repeat="f in [1,2,3]">item {{ f }}, index {{ $index }}</div>

<!-- replaced -->
<div ng-repeat="f in [1,2,3]">item 1, index 0</div>
<div ng-repeat="f in [1,2,3]">item 2, index 1</div>
<div ng-repeat="f in [1,2,3]">item 3, index 2</div>

When you compile it again in the link function, each of the 3 ng-repeats is triggered, making 3 identical copies, 9 total.

The solution

Keep your popover-template in a separate file so it is not compiled on page load. You can then load it with the $templateCache service.

In general, just make sure you don't compile your HTML multiple times.




回答2:


Instead using the compiled html for the popover template, load the template using $http or templateCache.

The HTML:

<span popover>Click me</span>
<script type="text/ng-template" id="popover.html">
  <div class="popover-template">
    <div class="template-title">
      <strong>{{ x.name }} and {{ y.name }}</strong>
    </div>
    <div class="template-content">
      <div>
        <pre>f in [1,2,3] track by $index</pre>
        <div ng-repeat="f in [1,2,3]">
          item {{ f }}, index {{ $index }}
        </div>
      </div>
    </div>
  </div>
</script>

The Javascript:

angular.module('app',[]).directive('popover', function($compile, $timeout, $templateCache){
    return {
        link: function(scope, element, attrs) {

            $timeout(function() {
                // grab the template (this is the catch)
                // you can pass the template name as a binding if you want to be loaded dynamically

                var tpl = angular.element($templateCache.get('popover.html'));

                // grab popover parts of template
                var template = {
                    title: tpl.find('.template-title').contents(),
                    content: tpl.find('.template-content').contents()
                };

                // render template with angular
                var content = $compile(template.content)(scope);
                var title = $compile(template.title)(scope); 

                $(element).popover({
                    html: true,
                    placement: "right",
                    content: content,
                    title: title
                });

                scope.$digest()
            });

        }

    };
});

Also, I have made this plunker with an working example: http://embed.plnkr.co/IoIG1Y1DT8RO4tQydXnX/



来源:https://stackoverflow.com/questions/26578446/angularjs-ng-repeat-applied-multiple-times-in-compiled-directive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!