【问题标题】:ng-repeat changing sort order on all items when new item added to model将新项目添加到模型时,ng-repeat 更改所有项目的排序顺序
【发布时间】:2014-05-23 07:24:15
【问题描述】:

我创建了一个“tessellate”指令,可以让您包装多个 div。

<tessellate columns="4">
  <div class="thumbnail" ng-repeat="item in items track by item.id">
      {{item.Name}}<br />
      {{item.Summary}}
  </div>
</tessellate>

一次需要一个 div 并将其附加到指定列数中最短的列,以创建镶嵌/马赛克效果。

在此处查看 plunkr:http://plnkr.co/edit/ur0bVCFRSz1UbRHeLjz8?p=preview

问题在于,当模型发生变化时,ng-repeat 使用 div 在 DOM 中出现的顺序 而不是模型中的顺序来重绘元素。您可以看到项目首先排序正确,然后单击Add 后,它会水平排序第一列中的项目,然后是下一列中的项目,等等。

如何防止 ng-repeat 使用 DOM 顺序重绘元素?我已经尝试添加 orderBy item.id,但没有帮助。

var app = angular.module('app', []);

app.controller('itemController', ['$scope', function ($scope) {
    $scope.items = [
             { id:"1", Name:"Item1", Summary:"This is the summary of Item1" },
             { id:"2", Name:"Item2", Summary:"This is the summary of Item2. Some extra text on item two to test different heights." },
             { id:"3", Name:"Item3", Summary:"This is the summary of Item3" },
             { id:"4", Name:"Item4", Summary:"This is the summary of Item4. Some extra text on item four to test different heights." },
             { id:"5", Name:"Item5", Summary:"This is the summary of Item5. Some extra text on item five to test different heights. Some extra text on item to test different heights." },
             { id:"6", Name:"Item6", Summary:"This is the summary of Item6" },
             { id:"7", Name:"Item7", Summary:"This is the summary of Item7. Some extra text on item seven to test different heights." },
             { id:"8", Name:"Item8", Summary:"This is the summary of Item8" },
             { id:"9", Name:"Item9", Summary:"This is the summary of Item9. Some extra text on item nine to test different heights." },
             { id:"10", Name:"Item10", Summary:"This is the summary of Item10. Some extra text on item ten to test different heights." },
             { id:"11", Name:"Item11", Summary:"This is the summary of Item11" },
             { id:"12", Name:"Item12", Summary:"This is the summary of Item12. Some extra text on item to test different heights." },
             { id:"13", Name:"Item13", Summary:"This is the summary of Item13" },
             { id:"14", Name:"Item14", Summary:"This is the summary of Item14. Some extra text on item to test different heights." },
             { id:"15", Name:"Item15", Summary:"This is the summary of Item15. Some extra text on item to test different heights. Some extra text on item to test different heights." },
             { id:"16", Name:"Item16", Summary:"This is the summary of Item16" },
             { id:"17", Name:"Item17", Summary:"This is the summary of Item17. Some extra text on item to test different heights." },
             { id:"18", Name:"Item18", Summary:"This is the summary of Item18" }
             ];
    $scope.inc = $scope.items.length;
    $scope.add = function() {
        $scope.inc = $scope.inc + 1;
        $scope.items.push({ id: $scope.inc, Name: "New Item" + $scope.inc, Summary:"New Summary" });
    };
}]);

app.directive('tessellate', [function () {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: {
            columns: '='
        },
        controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
            $scope.numberToArray = function (num) {
                return new Array(num);
            };
        }],
        link: function (scope, elem, attrs, ctrl) {

            scope.$watch(function () {
                return elem.children().first().height();
            }, function (height) {
                if (height > 0) {
                    var containers = elem.children();
                    var transcludedDivsContainer = containers.first();
                    var targetColumns = containers.eq(1).children();

                    // Add the transcluded divs one at a time into the shortest column.
                    angular.forEach(transcludedDivsContainer.children(), function (div) {
                        var shortCol = null;
                        angular.forEach(targetColumns, function (col) {
                            col = angular.element(col);
                            if (shortCol === null || col.height() < shortCol.height()) {
                                shortCol = col;
                            }
                        });
                        shortCol.append(div);
                    });
                }
            }
            );
        },
        templateUrl: "tessellateTemplate.html"
    };
}]);

【问题讨论】:

  • 我认为问题不在于 ng-repeat。问题的根源在于您的指令。该指令按它们的 DOM 顺序获取项目并相应地设置它们,而 ng-repeat 将它们置于“正确”的顺序中。只需在名称旁边添加{{$index}}(例如{{item.Name}}--||--{{$index}})并亲自查看索引是否良好。我没有深入研究您的指令,但冲突由此产生,我可以看到您正在查看 DOM 结构以对元素进行排序。
  • @yccteam,但是如果我单步执行代码,我可以看到订单在我的 $watch 被击中之前就已经搞砸了。我相信 ng-repeat 会爬取 DOM 以获取现有元素,因此它不必重新创建它们,但假定元素的顺序与它第一次运行时的顺序相同。
  • 你是对的。这是我以前从未尝试过的场景,但 Ben 在 blog post 中对此进行了解释

标签: javascript angularjs angularjs-directive angularjs-ng-repeat transclusion


【解决方案1】:

我把你的 plunkr 弄乱了。我认为它现在可以按照您想要的方式工作。

http://plnkr.co/edit/1y8jE0SLuJK6XTNRBKF3?p=preview

主要修复它是通过索引对 dom 元素列表进行排序,为此我将 $index 添加到元素的数据索引属性中。

【讨论】:

  • 从我的手机看它看起来不错,当我回到我的办公桌时我会检查我的桌面。你怎么知道这样设置 index 属性?
  • 哦,没关系。我以为您正在覆盖某些 ng-repeat 行为,但我看到您自己使用索引对元素进行排序。这很好用,但我很惊讶 ng-repeat 删除并替换了所有元素,即使其中只有一个元素发生了变化。你知道这是正常的还是这段代码强迫 ng-repeat 做的事情?我想知道是否可以对其进行调整以仅影响新的或更新的元素以提高性能。
  • 查看 ng-repeat 代码似乎只是对更改进行操作。仍然在列表中的元素不应被删除/重新添加到 dom。为了使曲面细分更快,您可能必须使用一些缓存技术,例如 ng-repeat 正在做的事情。现在,每次添加某些内容时,它都会重新细分整个列表。需要保留镶嵌状态的某种表示,恢复它,然后添加新元素,而不是每次都重新计算所有元素。
  • 嗯,这对我来说已经足够了。虽然,我知道这是一个单独的问题,但如果你从数组中删除一个项目,顺序仍然会混乱。
猜你喜欢
  • 2015-10-15
  • 1970-01-01
  • 2016-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-22
相关资源
最近更新 更多