Angular $parse在过滤/排序以整数开头的哈希值时出错

Angular $parse error when filtering/ordering on hash values that start with integers

本文关键字:开头 整数 哈希值 出错 排序 parse 过滤 Angular      更新时间:2023-09-26

我有一个相当复杂的角order过滤器结构,它工作得非常好,除了当表达式传递给过滤器函数以以整数开头的散列开始时。

这里有一个小提琴,可以很好地捕捉到这个问题。

在:

结构中使用数组
[{'ced8d91c7921a884a131fcc7086239':{'value':'somevalue1'},
  '0a9308d3092d092718e457d927f110': {'value':'anotherval'}},
 {'ced8d91c7921a884a131fcc7086239':{'value':'axxfi'},
  '0a9308d3092d092718e457d927f110': {'value':'zziojasf'}}];

和'somehash结构中的过滤器谓词。如果散列以字母开头,Value '将起作用。

我过滤哈希id而不是一个更容易读懂的结构的原因是,数据是动态的,angular不知道它需要过滤的属性名称是什么。

当尝试在JS中使用$filter.

进行过滤时,这个错误仍然存在。

查看调用堆栈,问题似乎发生在Lexer.lex函数中,特别是在这里:

else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
    this.readNumber();

有什么解决办法吗?

编写自定义哈希键过滤器

我将始终使用自定义过滤器,而不是与Lexer或$parse抗争。这样我们就可以绕过Lexer并修复我们的问题,然后直接向Angular团队提交一个bug。

注意:这适用于AngularJS 1.2.1,也就是你所选择的版本。我不确定它是否能与任何当前版本的AngularJS一起工作。

这只是一个简单的psuedo关联数组排序器,它接受一个谓词字符串表达式来钻取要比较的值。

我们需要将arrOfObjects中的值压入到我们的输出arr中以避免引用。我们不能用angular.copy,因为它会燃烧到许多消化循环,嗯…呕吐。

下面是过滤器,这里是一个更新的小提琴。

angular.module('orderByFail').filter('orderByHashKey', [
  function orderByHashKeyFilter() {
    // params: array of objects, the property expression as predicate
    //         and reverse
    // NOTE: the property expression is not an Angular expression
    // it is a dot-notation property string expression (ex: obj.key1.key2)
    return function(arrOfObjects, propertyExp, reverse) {
      // if there is no predicate then just return the array
      // the whole reason we are using this is for hash key predicates
      if (typeof propertyExp === 'undefined') return arrOfObjects;
      propertyExp = propertyExp.split('.');
      var len = propertyExp.length;
      var arr = [];
      for (var i = 0; i < arrOfObjects.length; i++) { arr.push(arrOfObjects[i]); }
      arr.sort(function (a, b) {
        var i = 0;
        // drill down to the values we want
        while( i < len ) {
          a = a[propertyExp[i]];
          b = b[propertyExp[i]];
          i++;
        }
        // this is just a slightly faster bitwise solution
        // you can comment this out and use the ternary form below if you prefer
        if (a < b) {
          return -!reverse | 1;
        } else if (a > b) {
          return -!!reverse | 1;
        } else {
          return 0;
        }
        /*
        if (a < b) {
          return reverse ? 1 : -1;
        } else if (a > b) {
          return reverse ? -1 : 1;
        } else {
          return 0;
        }
        */
      });
      return arr;
    }
  }
]);

您可以编写getter函数而不是指定谓词。例如http://jsfiddle.net/c6Ldmphu/2/

(function () {
  angular.module( 'orderByFail', [  
]);
    
function MyCtrl($scope, $filter, $log) {
    $scope.reverse = true;
    $scope.sortColumn2 = '0a9308d3092d092718e457d927f110.value';
    
    $scope.entries = [{'ced8d91c7921a884a131fcc7086239':{'value':'somevalue1'},
                       '0a9308d3092d092718e457d927f110': {'value':'anotherval'}
                     },
                   {'ced8d91c7921a884a131fcc7086239':{'value':'axxfi'},
                    '0a9308d3092d092718e457d927f110': {'value':'zziojasf'}
                   }];
    
    $scope.sort1 = function() {
        $scope.reverse = !$scope.reverse;
        $scope.sortColumn1 = 'ced8d91c7921a884a131fcc7086239.value'
    }
    
     $scope.sort2 = function() {
        $scope.reverse = !$scope.reverse;
        $scope.sortColumn2 = '0a9308d3092d092718e457d927f110.value'
     }
    
     $scope.sort = function(val) {
         var cols = $scope.sortColumn2.split('.');
         angular.forEach(cols, function(col, colKey) {
             val = val[col];
         });
         return val;
     }
}
    
angular
  .module('orderByFail')
  .controller('MyCtrl', MyCtrl)
  ;
})(); 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<div ng-app="orderByFail">
  <div ng-controller="MyCtrl">
    <button ng-click="sort1()">This sort works</button>
    <div ng-repeat="entry in entries | orderBy:sortColumn1:reverse">{{entry['ced8d91c7921a884a131fcc7086239'].value}}</div>
    <button ng-click="sort2()">This sort will error in console</button>
    <div ng-repeat="entry in entries | orderBy: sort:reverse">{{entry['0a9308d3092d092718e457d927f110'].value}}</div>
  </div>
</div>