嵌套的ng-repeat在10级深度后给出错误:10 $ digest()迭代达到

时间:2016-05-23 01:06:39

标签: angularjs

收到此错误:Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!

我使用AngularJS创建了一个reddit样式的嵌套注释系统。但是在10级之后我在我的控制台上得到一个非常难看的错误,如下所示:

enter image description here

这恰好发生在10个深度之后:

enter image description here

嵌套注释是如下所示的指令:

commentCont.tpl.html:

<div class="comment-cont">
    <div
        class="upvote-arrow"
        ng-show="!collapsed"
        ng-click="vote()"
        ng-class="{'active' : node.votedByMe}"
    >
        <div class="upvote-arrow-top"></div>
        <div class="upvote-arrow-bottom"></div>         
    </div>      
    <div class="wrapper">
        <div class="info">
            <span class="collapse" ng-click="collapsed = !collapsed">[ {{collapsed ? '+' : '–'}} ]</span>
            <span ng-class="{'collapsed-style' : collapsed}">
                <a ui-sref="main.profile({id: node.userId})">{{node.username}}</a>
                <span>{{node.upvotes}} point{{node.upvotes != 1 ? 's' : ''}}</span>
                <span mg-moment-auto-update="node.createdTime"></span>
                <span ng-show="collapsed">({{node.children.length}} child{{node.children.length != 1 ? 'ren' : ''}})</span>
            </span>
        </div>          
        <div class="text" ng-bind-html="node.comment | autolink | nl2br" ng-show="!collapsed"></div>
        <div class="reply" ng-show="!collapsed">
            <span ng-click="formHidden = !formHidden">reply</span>
        </div>          
    </div>
    <div class="input-area" ng-show="!collapsed">
        <form ng-show="!formHidden" name="form" autocomplete="off" novalidate ng-submit="submitted = true; submit(formData, form)">

            <div class="form-group comment-input-feedback-branch has-error" ng-show="form.comment.$invalid && form.comment.$dirty">
                <div ng-messages="form.comment.$error" ng-if="form.comment.$dirty">
                    <div class="input-feedback" ng-message="required">Comment text is required.</div>
                    <div class="input-feedback" ng-message="maxlength">Comment text cannot exceed 2500 characters.</div>
                </div>
            </div>

            <div
                class="form-group"
                ng-class="{ 'has-error' : form.comment.$invalid && form.comment.$dirty, 'has-success' : form.comment.$valid }"
            >
                <textarea
                    name="comment"
                    class="textarea comment-cont-textarea"
                    ng-model="formData.comment"
                    required
                    ng-maxlength="2500"
                    textarea-autoresize
                ></textarea>
            </div>
            <div class="form-group">
                <mg-button-loading
                    mgbl-condition="awaitingResponse"
                    mgbl-text="Save"
                    mgbl-loading-text="Saving..."
                    mgbl-class="btn-blue btn-small"
                    mgbl-disabled="!form.$valid"
                ></mg-button-loading>
                <mg-button-loading
                    mgbl-text="Cancel"
                    mgbl-class="btn-white btn-small"
                    ng-click="formHidden=true;"
                ></mg-button-loading>
            </div>                  
        </form>
    </div>
    <div ng-show="!collapsed">
        <div ng-repeat="node in node.children" ng-include="'commentTree'"></div>
    </div>
</div>

commentCont.directive.js:

(function () {
    'use strict';

    angular
        .module('app')
        .directive('commentCont', commentCont);

    /* @ngInject */
    function commentCont ($http, user, $timeout) {

        return {
            restrict: 'E',
            replace: true,
            scope: {
                node: '='
            },
            templateUrl: 'app/post/commentCont.tpl.html',
            link: function (scope, element, attrs) {

                var textarea = element.querySelector('.comment-cont-textarea');
                var voteOK = true;
                var action = '';

                var userInfo = user.get();

                scope.formHidden = true; // Do not ng-init="" inside an ng-repeat.
                scope.collapsed = false; // Do not ng-init="" inside an ng-repeat.

                // Autofocus textarea when reply link is clicked.
                scope.$watch('formHidden', function(newValue, oldValue) {
                    if (newValue !== true) {
                        $timeout(function() {
                            textarea[0].focus();
                        });                     
                    }
                });

                scope.submit = function (formData, form) {
                    if (form.$valid) {
                        scope.awaitingResponse = true;
                        formData.parentId = scope.node.id;
                        formData.postId = scope.node.postId;

                        formData.type = 4;
                        formData.fromUsername = userInfo.username;
                        formData.toId = scope.node.userId;
                        formData.fromImage = userInfo.thumbnail36x36.split('/img/')[1];

                        // console.log(formData);

                        $http.post('/api/comment', formData)
                            .then(function (response) {
                                scope.awaitingResponse = false;

                                if (response.data.success) {
                                    if (response.data.rateLimit) {
                                        alert(rateLimitMessage);
                                        return false;
                                    }
                                    // id and createdTime is sent with response.data.comment.
                                    var c = response.data.comment;
                                    var newCommentNode = {
                                        id: c.id,
                                        userId: userInfo.id,
                                        username: userInfo.username,
                                        parentId: formData.parentId,
                                        comment: formData.comment,
                                        upvotes: 0,
                                        createdTime: c.createdTime,
                                        votedByMe: false,
                                        children: [],
                                        postId: scope.node.postId
                                    };
                                    // console.log('user', user.get());
                                    // console.log('scope.node', scope.node);
                                    // console.log('response', response);
                                    // console.log('newCommentNode', newCommentNode);
                                    formData.comment = '';
                                    form.comment.$setPristine();
                                    scope.formHidden = true;
                                    scope.node.children.unshift(newCommentNode);                                    
                                }
                            });
                    }
                };

                scope.vote = function() {
                    if (voteOK) {
                        voteOK = false;
                        if (!scope.node.votedByMe) {
                            scope.node.votedByMe = true;
                            action = 'add';
                        } else {
                            scope.node.votedByMe = false;
                            action = 'remove';
                        }
                        var data = {
                            commentId: scope.node.id,
                            action: action
                        };
                        $http.post('/api/comment/vote', data)
                            .then(function (response) {
                                // console.log(response.data);
                                voteOK = true;
                                if (action === 'add') {
                                    scope.node.upvotes++;
                                } else {
                                    scope.node.upvotes--;
                                }
                            });                     
                    }
                };
            }
        };
    }

}());

正在调用树:

<script type="text/ng-template" id="commentTree">
    <comment-cont
        node="node"
    ></comment-cont>
</script>
<div ng-repeat="node in tree[0].children" ng-include="'commentTree'"></div>

如何获得超过10级嵌套ng-repeat而不会出现这样的错误?

2 个答案:

答案 0 :(得分:3)

$ digest()的默认实现限制为10次迭代。如果在10次迭代后范围仍然很脏,则会从$ digest()抛出错误。

以下陈述是将摘要迭代限制配置为20的一种方法。

var app = angular.module('plunker', [], function($rootScopeProvider) { 
  $rootScopeProvider.digestTtl(20); // 20 being the limit of iterations. 
});

但是你应该考虑稳定你的模型,而不是配置迭代的限制。

答案 1 :(得分:0)

我正在处理类似的问题。最终得到以下指令:

(function () {
'use strict';

angular
    .module('app')
    .directive('deferDom', ['$compile', ($compile) => {
        return {
            restrict: 'A',
            compile: (tElement) => {

                // Find, remove, and compile the li.node element.
                let $el = tElement.find( "li.node" );
                let transclude = $compile($el);
                $el.remove();

                return function($scope){
                    // Append li.node to the list.
                    $scope.$applyAsync(()=>{
                        tElement.append(transclude($scope));
                    });
                }

            },
        };
    }]);

})();