AngularJS orderBy - Chromium浏览器中疯狂的Array.prototype.sort行为 - 无法清除排序

时间:2015-04-18 10:44:12

标签: javascript arrays angularjs sorting

在我的一个项目中有些东西让我疯狂。

我有一些非常类似的代码(我试图使它与我的相似,使用我使用的所有JS文件等,但是这个Plnkr中的代码似乎并不重要,因为你会看到因为我无法在那里重现...): http://plnkr.co/edit/SCUCsxLgu2zFzMhZ0whs?p=preview

我的问题是,当我设置predicate=''时,排序不会100%清除。 Angular似乎改变了我的元素的顺序(这张图片是predicate=''):

enter image description here

当我删除orderBy时,所有内容都按顺序排列(就像它是从我的存储库中提供服务的那样)。在用orderBy尝试数以百万计不同的事情之后,我无法完全清楚这一点。

我发现这个案例与我有一些相似之处:AngularJS ngRepeat orderBy in Chrome,但事实证明我的案子与该问题无关。

我是一个耐心的人,所以我进入angular.js文件(AngularJS v1.3.15)并开始调试。这是最重要的一句话:

return slice.call(array).sort(reverseComparator(comparator, reverseOrder));

直到slice.call(array)数组排序良好。执行sort时结果错误。所以我这样做了:

function comparator(o1, o2) {
  for (var i = 0; i < sortPredicate.length; i++) {
    var comp = sortPredicate[i](o1, o2);
    if (comp !== 0){
      console.log(7);  // <---------------------
      return comp;
    }
  }
  console.log(0);      // <---------------------
  return 0;
}

function reverseComparator(comp, descending) {
  return descending
      ? function(a, b) {return comp(b,a);}
      : comp;
}

猜猜:当predicate=''为零时,我进入我的控制台。 另外,输入:

slice.call(array).sort

...返回:

function sort() { [native code] }

所以,我不知道为什么会发生这种排序。 它似乎只发生在基于Chromium的浏览器中(我尝试过Chrome和Opera)。 FF和IE11按预期清除排序。

现在,我认为这是一个Chromium bug,但我还是没有设法在那个Plnkr中重现它,我想知道为什么(数据中可能有不同的东西?)...

我也尝试停用我的Chrome扩展程序,但这似乎没有任何区别。

Chromium和Angular现在都很有名。还有其他人注意过这个吗?

有没有人知道这里有什么问题?

更新

行。我想我已经设法通过向我的阵列添加更多项目来重现它。看这里: http://plnkr.co/edit/SCUCsxLgu2zFzMhZ0whs?p=preview

(派对时间!:D)

我要去向Angular团队报告。他们是谷歌。他们也可以修复Chromium。 :d

3 个答案:

答案 0 :(得分:3)

来自ECMAScript specification

  

排序不一定稳定(也就是说,比较相等的元素不一定保持原始顺序)。

所以它是浏览器中的错误。

Angular代码确实考虑了一个空谓词:

if (predicate === '') {
  // Effectively no predicate was passed so we compare identity
  return reverseComparator(compare, descending);
}

compare函数

function compare(v1, v2) {
  var t1 = typeof v1;
  var t2 = typeof v2;
  if (t1 === t2 && t1 === "object") {
    v1 = objectToString(v1);
    v2 = objectToString(v2);
  }
  ...

objectToString正在

function objectToString(value) {
  if (value === null) return 'null';
  if (typeof value.valueOf === 'function') {
    value = value.valueOf();
    if (isPrimitive(value)) return value;
  }
  if (typeof value.toString === 'function') {
    value = value.toString();
    if (isPrimitive(value)) return value;
  }
  return '';
}

由于您的对象既没有拥有 valueOftoString方法,所以它们最终相等(函数可能会返回"[object Object]")。这让我们回到了ECMAScript规范。

答案 1 :(得分:2)

我将最终采用的解决方案是使用这些自定义过滤器:

(function () {

    function isNullOrEmpty(str) {
        return str === '' || str === undefined || str === null;
    }

    angular.module('CustomFilters', [])
        .filter('okOrderBy', ['$filter', function ($filter) {

            return function (array, predicate, reverse) {
                if (isNullOrEmpty(predicate)) return array;
                return $filter('orderBy')(array, predicate, reverse);
            };
        }
        ])
        .filter('arrayOrderBy', [function () {

            function Comparator(predicate, reverse) {
                return reverse ? function (a, b) {
                    a = a[predicate];
                    b = b[predicate];
                    if (a === b) return 0;
                    return a < b ? 1 : -1;
                } : function (a, b) {
                    a = a[predicate];
                    b = b[predicate];
                    if (a === b) return 0;
                    return a < b ? -1 : 1;
                };
            }

            return function (array, predicate, reverse) {
                if (isNullOrEmpty(predicate)) return array;
                return array.slice(0).sort(new Comparator(predicate, reverse));
            };
        }
        ]);

})();

第二个独立于Angular过滤器,我更喜欢它。

但它仅适用于对象数组,谓词只是属性名称,并且不允许&#34;多列&#34;排序。 虽然第一个应该适用于所有事情(我猜)。

我有一个很大的困境,我应该接受哪个答案,因为所有其他答案都是有用和有趣的。我希望我能不止一次地投票给他们。我正在考虑选择这个答案(我知道......我很自私......)只是因为它解决了问题,而且使用的时间比用户提供的更短“#34;你&#34;。

答案 2 :(得分:1)

另一种方法。当你不想要时,不要让排序运行。

很高兴的角度让你在那里写任何东西。

<tr ng-repeat="friend in (predicate ? (friends | orderBy:predicate:reverse) : friends)">
  <td>{{friend.name}}</td>
  <td>{{friend.phone}}</td>
  <td>{{friend.age}}</td>
</tr>

http://plnkr.co/edit/KIdAKPtA3JDtRMDD50VI?p=preview