【问题标题】:AngularJS orderby with empty field带有空字段的AngularJS orderby
【发布时间】:2017-10-18 20:43:22
【问题描述】:

我正在订购我的数据,它的工作一切正常,除了某些字段为空或没有值。订购时,这些空白字段首先出现。例如,在订购数字时,我们会在获得“0”值之前获得一个巨大的空列表。

我就是这样做的:

ng-click="predicate = 'name'; reverse=!reverse"

ng-repeat="name in names | orderBy:predicate:reverse"

JSFiddle:http://jsfiddle.net/JZuCX/1/

有没有简单优雅的方法来解决这个问题?无论如何,我都希望空字段排在最后。

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    如何对字符串进行排序:

    item in (items|orderBy:['!name', 'name'])
    

    优点(除了更简洁之外)是它将nullundefined 与空白字符串进行排序。

    在我的例子中,我希望将空白、空值和未定义值放在一起(默认情况下,空值和未定义值排在底部),所以我使用了:

    item in (items|orderBy:['!!name', 'name'])
    

    【讨论】:

    • 这太棒了!如果属性存储在变量中,我怎么能做到这一点? (items|orderBy:['!' + myvarname, myvarname]) 中的项目不起作用
    • 简直太棒了,甚至可以使用参数作为顺序,即element in elements | orderBy:['!!' + order, order]:reverse
    • @jorgetutor - 我想你已经把括号去掉了。应该是item in items | orderBy: ['!'+myvarname, myvarname]
    • 这适用于除 0(零)之外的数字和字符串。它将 0 与 nullundefined 值分组。有没有办法将零作为合法值?
    • 这是一个使用字符串+数字排序的版本:jsfiddle.net/kmturley/cezjga46
    【解决方案2】:

    我会编写一个过滤器,从有序数组中获取具有空名称的项目并将它们放在末尾:

    <li ng-repeat="item in (items|orderBy:'name'|emptyToEnd:'name')">{{item.name}}</li>
    

    代码可能如下所示:

    .filter("emptyToEnd", function () {
        return function (array, key) {
            if(!angular.isArray(array)) return;
            var present = array.filter(function (item) {
                return item[key];
            });
            var empty = array.filter(function (item) {
                return !item[key]
            });
            return present.concat(empty);
        };
    });
    

    Working example.

    顺便说一句,您的小提琴不包含任何相关代码。您是否使用了错误的链接?

    更新 2: Your fiddle with my filter.

    【讨论】:

    • 对不起,在正确的小提琴中编辑。我将如何将您的示例与我的小提琴相结合?
    • @deekay,做myApp.filter("emptyToEnd...
    • 我添加了一个链接来摆弄集成过滤器。
    • 酷就是这样!为什么我收到错误 TypeError: Cannot call method 'filter' of undefined at Object. in my app?
    • 如果你想包含零作为一个值并且不被归类为空交换这个位:var present = array.filter((item) => { return item[key] || item[key] === 0; }); var empty = array.filter((item) => { return !item[key] && item[key] !== 0; });
    【解决方案3】:

    下来! :D

    此解决方案扩展了 angularJs orderBy 过滤器的正常功能,以采用第三个参数,指定是否反转空值和未定义值的正常排序。它观察它传递的属性名称(不仅仅是一个),并且不会像其他一些解决方案那样一秒钟迭代项目。它是这样使用的:

    <li ng-repeat="item in (items|orderBy:'name':false:true)">{{item.name}}</li>
    

    我找到了一堆线程,其中一些与orderBy 没有直接关系,并将他们的技术和我自己的一些技巧编译成这个:

    angular.module('lib')
    .config(['$provide', function ($provide) {
        $provide.decorator('orderByFilter', ['$delegate', '$parse', function ($delegate, $parse) {
            return function () {
                var predicates = arguments[1];
                var invertEmpties = arguments[3];
                if (angular.isDefined(invertEmpties)) {
                    if (!angular.isArray(predicates)) {
                        predicates = [predicates];
                    }
                    var newPredicates = [];
                    angular.forEach(predicates, function (predicate) {
                        if (angular.isString(predicate)) {
                            var trimmed = predicate;
                            if (trimmed.charAt(0) == '-') {
                                trimmed = trimmed.slice(1);
                            }
                            var keyFn = $parse(trimmed);
                            newPredicates.push(function (item) {
                                var value = keyFn(item);
                                return (angular.isDefined(value) && value != null) == invertEmpties;
                            })
                        }
                        newPredicates.push(predicate);
                    });
                    predicates = newPredicates;
                }
                return $delegate(arguments[0], predicates, arguments[2]);
            }
        }])
    }]);
    

    要逐字使用此代码,请将“lib”指定为您的应用程序的依赖项。

    归功于:

    【讨论】:

    • 这就像一个魅力。这是迄今为止我发现的唯一可以与多个谓词一起正常工作的解决方案。而且启动起来超级干净。
    • 虚幻!扩展 Angular 的功能是一种更好的方法。
    • 由于 AFAICT 智能表不允许您传递其他参数,因此我对其进行了调整以添加 var sortDescending = arguments[2],然后将检查从 invertEmpties 更改为 sortDescending;将 invertEmpties 设置为 true,此装饰器在使用智能表时无论排序方向如何,都在底部保持 null/empty。
    • 这仅检查空值...但可以通过修改轻松修改为也将空字符串和未定义值放在最后:(angular.isDefined(value) &amp;&amp; value != null)
    【解决方案4】:

    我认为没有“开箱即用”的解决方案。我很容易出错。 这是我尝试使用函数作为谓词的解决方案:

    ng-repeat="name in names | orderBy:predicate"
    

    在您的控制器内部:

    $scope.predicate = function(name) {
        return name === '' ? 'zzzzzzz' : !name; 
        /* The 'zzzzzz' forces the empty names to the end, 
          I can't think of a simpler way at the moment. */
    }
    

    【讨论】:

    • 您可以使用'~'(ASCII 126)代替'zzz...'
    【解决方案5】:

    在Klaster_1的解决方案之外,增加一个额外的参数,让过滤器更通用:

    http://jsfiddle.net/Zukzuk/JZuCX/27/

    实施

    <tr ng-repeat="name in (names | orderBy:predicate:reverse | orderEmpty:'name':'toBottom')">
    

    过滤器

    .filter('orderEmpty', function () {
        return function (array, key, type) {
            var present, empty, result;
    
            if(!angular.isArray(array)) return;
    
            present = array.filter(function (item) {
                return item[key];
            });
    
            empty = array.filter(function (item) {
                return !item[key]
            });
    
            switch(type) {
                case 'toBottom':
                    result = present.concat(empty);
                    break;
                case 'toTop':
                    result = empty.concat(present);
                    break;
    
                    // ... etc, etc ...
    
                default:
                    result = array;
                    break;
            }
            return result;
        };
    });
    

    谢谢 Klaster_1!

    【讨论】:

      【解决方案6】:

      排序和反向排序,使用可变排序列,将未定义保持在底部,甚至低于负值

      我喜欢上面肖恩回答的优雅!我需要让我的用户能够选择要排序的列,以及选择排序方向,但仍然需要未定义的列到底部,即使有负数。

      Sean 修复负数的关键见解是 !!。如果要进行正向排序,请使用 '!'+predicate;如果要进行反向排序,请使用 '!!'+predicate。

      下面的 sn-p 演示了这一点。顺便说一句,我已经将设置谓词(要排序的属性的选择)和反转的变量放在对象(“d”)中,这样我们就不会遇到奇怪的范围问题。您的环境中可能不需要“d.”。

      此外,您可能希望使用比页面底部的蹩脚按钮更好的东西来控制您的排序谓词和方向。然而,这使代码的关键部分易于阅读。

      function mainController($scope) {
        $scope.userArray = [
        { name: "Don", age: 20 },
        { name: "Bob", age: 30, height: 170 },
        { name: "Abe", age: 40, height: 160 },
        { name: "Zoe", age: 70 },
        {              age: 70, height: 155 },
        { name: "Shorty",age:45,height: -200},
        { name: "TwinkleInEye", age: -1, height: 152 }
        ]
      
        $scope.d = {}; // Create an object into which info can be stored and not trashed by Angular's tendency to add scopes
        $scope.d.predicate = "name"; // This string is the name of the property on which to sort
        $scope.d.reverse = false; // True means reverse the sort order
      }
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
      
      <body ng-app="" ng-controller="mainController">
        <div ng-repeat="user in (userArray | orderBy: (d.reverse ?['!!'+d.predicate,d.predicate]:['!'+d.predicate,d.predicate]) : d.reverse)">
      
          Name {{ user.name }} : Age {{ user.age }} : Height {{ user.height }}
        </div>
      
        <br/>
      
        <button ng-click="d.predicate='name';">Name</button>
        <button ng-click="d.predicate='age';">Age</button>
        <button ng-click="d.predicate='height';">Height</button> Currently: {{d.predicate}}
       <br/> Leave undefined at bottom, but otherwise: 
        <button ng-click="d.reverse= !d.reverse;">Reverse</button> Currently: {{d.reverse}}
      
      </body>

      【讨论】:

      • 我认为这是最好的解决方案,因为它不需要重新发明排序顺序。
      【解决方案7】:

      @Klaster_1 确实在做某事,但只要我需要一个嵌套值,过滤器就会停止工作。此外,如果我是反向排序,我仍然希望我的空值显示在 0 之前。我添加了 $parse 来处理嵌套键,并添加了一个反向参数,以便我知道何时将空值放在顶部。

      .filter("emptyToEnd", function ($parse) {
          return function (array, key, reverse) {
              if(!angular.isArray(array)) return;
              var keyFn = $parse(key);
              var present = [];
              var empty = [];
      
              angular.forEach(array, function(item){
                var val = keyFn(item);
                if(angular.isUndefined(val) || val === null) {
                  empty.push(item);
                } else {
                  present.push(item);
                }
              });
      
              if (reverse) {
                return present.concat(empty);
              } else {
                return empty.concat(present);
              }
          };
      });
      

      【讨论】:

      • 请注意,这不适用于否定键。这意味着当您使用“-keyToSortOn”时,此过滤器会落在它的脸上。
      【解决方案8】:

      我不知道为什么其他答案建议将空值记录放在底部,如果我想正常排序,意味着按 ASC 顺序将所有空值放在顶部,按 DESC 顺序将所有空值放在底部,我在这里尝试了其他答案,但无法帮助我更改代码以将数组中的 null 转换为 '',现在它可以像这样顺利运行:

      $scope.convertNullToBlank = function (array) {
        for (var i = 0; i < array.length; i++) {
           if (array[i].col1 === null)
             array[i].col1 = '';
      
           if (array[i].col2 === null)
              array[i].col2 = '';
        }
        return array;
      }
      

      【讨论】:

        【解决方案9】:

        我根据之前的解决方案创建了一个带有替代过滤器的要点: https://gist.github.com/360disrupt/1432ee1cd1685a0baf8967dc70ae14b1

        滤镜扩展了现有的角度滤镜:

        angular.module 'tsd.orderByEmptyLast', []
          .filter 'orderByEmptyLast', ($filter) ->
            return (list, predicate, reverse)->
              orderedList = $filter('orderBy')(list, if reverse then ['!' + predicate, '-' + predicate] else ['!' + predicate, predicate] )
              return orderedList
        

        在较新的 Angular 版本中,您可能需要包含 orderByFilter 而不是使用 $filter

        angular.module 'tsd.orderByEmptyLast', ['orderByFilter']
          .filter 'orderByEmptyLast', () ->
            return (list, predicate, reverse)->
              orderedList = orderByFilter(list, if reverse then ['!' + predicate, '-' + predicate] else ['!' + predicate, predicate] )
              return orderedList
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-12-05
          • 1970-01-01
          • 2016-03-15
          • 1970-01-01
          • 2013-03-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多