当新的$ digest周期触发时,指令的DOM操作不会被保存

时间:2014-03-14 20:54:53

标签: javascript angularjs angularjs-directive

我有一个自定义指令,在服务准备就绪时会收到通知,然后应该操作它的DOM。

DOM操作得很好,但每当我来回过滤ng-repeat时这个指令都在(当一个新的$ digest循环触发时),该指令恢复到其初始状态(没有修改的DOM,即它的编译状态) )。

一旦指令被服务和DOM操作通知,范围和DOM就不会改变。那么它的状态就是它的最终形式,而且每当新的$ digest周期触发时,我都希望DOM能够存在。

如何解决这个问题?

如何保存异步DOM操作,以便在下一个$ digest触发时它仍然存在?

我想这样做的原因是,在指令得到通知后数据不会改变,因此,不希望并且需要使用观察者。

感谢。

代码

angular.module('myApp').directive('myItem', function (_, myService) {
    var pricesToShow = 3,
        textToShow = 'Fetching Data';

    return {
        restrict: 'A',
        transclude: false,
        replace: true,
        scope: true,
        templateUrl: 'tpl/myItem.html',
        compile: function($templateElement) {
            // THIS IS THE STATE DIRECTIVE REVERTS TO WHENEVER ITS RERENDERED - WHY?
            var $pricesElem = $templateElement.children().eq(0).children().eq(-1),
                html= '';

            if (pricesToShow) {
                html = '<span>' + textToShow + '</span>';

                _.times(pricesToShow, function() {
                    html += '<div class="pricetag"><span class="sid">' + i + '</span> <span class="price"></span> <span class="currency"></span></div>';
                });

                $pricesElem.append(html);
            }

            return function($scope, $linkElement, $linkAttributes, ctrl) {
                var id = Math.random().toString(36).slice(2),
                    $priceTagElems = $linkElement.children().eq(0).children().eq(-1).children().slice(1);

                $scope.$on('$destroy', onDestroy);

                if (myService.getServices().isDoneProcessing) {
                    showPrices();
                } else {
                    myService.registerObserver(id, updatePrices);
                }

                function onDestroy() {
                    myService.unregisterObserver(id);
                }

                function showPrices(isAsyncCall) {

                    if (!isAsyncCall && !$scope.trip.prices) {
                        updatePrices();
                    } else if (!isAsyncCall && $scope.trip.prices) {
                        // DOM should now be in its final form - but appears to be in its compile form :(
                        return;
                    }

                    // THESE CHANGES TO DOM WON'T BE SAVED FOR THE NEXT $DIGEST - WHY?
                    _.each($scope.trip.prices, function(price, i) {
                        $priceTagElems.eq(i).children().eq(0)
                            .text(price.sid).next()
                            .text(price.value.toFixed()).next()
                            .text(price.currency);
                    });
                }

                function updatePrices(isNotificationCallback) {
                    if ($scope.trip.prices) { return; }

                    $scope.trip.prices = [];

                    _.each($scope.trip.values, function(item) {
                        $scope.trip.prices.push({'sid': item.sid, 'value': item.price.value, 'currency': item.price.currency});
                    });

                    $scope.trip.prices.sort(function(a, b) {
                        return a.value - b.value;
                    });

                    if (isNotificationCallback) {
                        $scope.$apply(function() {
                            $scope.trip.prices = _.first($scope.trip.prices, pricesToShow);
                            showPrices(true);
                        });
                    } else {
                        $scope.trip.prices = _.first($scope.trip.prices, pricesToShow);
                    }
                }
            };
        },
        controller: function($scope) {
            // Some logic ...
        }
    };

});

myItem.html

<div>
    <div>
        <div>..some bo-text spans..</div>
        <div>..some bo-text spans..</div>
        <div class="prices">..this will be replaced by compile function and in async callback function..</div>
    </div>
</div>

ng-repeat

<div bindonce="trip" ng-repeat="trip in trips | myFilter:customFilters | limitTo: 20 track by trip.key" my-item="">Replaced by directive</div>

myFilter

return function (trips, filter) {
    var filtered = [];
    _.each(trips, function(element) {
        var match = hasMatch(element, filter);
        if (match) {
           filtered.push(element);
        }
    });
    return filtered;
};

1 个答案:

答案 0 :(得分:0)

作为我们讨论的结论:

  • 逆转与limitTo过滤器结合的顺序意味着范围被销毁,而元素也是
  • 当entety重新进入已过滤列表时,使用其后编译状态的元素,也就是说没有在前一个链接函数范围生命周期中执行的dom转换,将调用链接函数

所有这些意味着发生的行为绝对正常。

作为解决方案,如果已经链接(如此转换),则不应删除元素,仅隐藏元素。不推荐在实体上克隆和存储某种方式。最好有一个完整的自定义过滤器标记实体已经显示一次,以便它们可以简单地隐藏在有序和有界列表中。