可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have this data:
[{"id":"42","firstname":"Sarah","lastname":"Dilby","age":"40","cars":"Yaris"}, {"firstname":"Jason","lastname":"Diry","age":"5","id":"5"}, {"id":"6","firstname":"Bilson","lastname":"Berby","age":"1","cars":"Tipo"}]
When I orderBy id or by age in an ng-repeat, it sorts the number as text. Since I can't find it written that this is an issue anywhere, I'm guessing there's a problem with my code. I have created this fiddle: http://jsfiddle.net/vsbGH/1/ Sorry about the template, but jsfiddle doesn't allow in the html box. Anyway, this is the code which loads and sorts the data:
//user data app.service('People', function() { var People = {}; People.details = [{"id":"42","firstname":"Sarah","lastname":"Dilby","age":"40","cars":"Yaris"}, {"firstname":"Jason","lastname":"Diry","age":"5","id":"5"}, {"id":"6","firstname":"Bilson","lastname":"Berby","age":"1","cars":"Tipo"}] return People; }); //list ctrl controllers.listCtrl = function ($scope,People) { $scope.people = People.details; $scope.sortList = function(sortname) { $scope.sorter = sortname; } }
And this is the ng-repeat part of the template:
<tr ng-repeat="person in people | orderBy:sorter "> <td>{{person.id | number}}</td> <td>{{person.firstname}} </td> <td>{{person.lastname}} </td> <td>{{person.age | number}}</td> <td>{{person.cars}} </td> </tr>
Many thanks if you can help me understand why the number data isn't sorting as numbers, and why it's sorting as text.
回答1:
I think the most appropriate solution is to format the numbers I have on my JSON objects correctly, ie not to wrap them in quotes. So:
[{"id":"42","firstname":"Sarah","lastname":"Dilby","age":"40","cars":"Yaris"}, {"firstname":"Jason","lastname":"Diry","age":"5","id":"5"}, {"id":"6","firstname":"Bilson","lastname":"Berby","age":"1","cars":"Tipo"}]
becomes:
[{"id":42,"firstname":"Sarah","lastname":"Dilby","age":40,"cars":"Yaris"}, {"firstname":"Jason","lastname":"Diry","age":5,"id":5}, {"id":6,"firstname":"Bilson","lastname":"Berby","age":1,"cars":"Tipo"}]
I'm guessing SergL's solution is good if it's not possible to correct the format of the JSON data.
To add to this, the issue in my specific case is to do with PHP's json_encode function on the server side. By default, it treats numbers as strings. To fix I had to add the JSON_NUMERIC_CHECK
option to the encode method in the PHP script:
json_encode($assoc_array,JSON_NUMERIC_CHECK);
回答2:
You don't have to modify your JSON. You can pass a function to orderBy filter like this:
$scope.sorterFunc = function(person){ return parseInt(person.id); }; <tr ng-repeat="person in people | orderBy:sorterFunc "> <td>{{person.id | number}}</td> <td>{{person.firstname}} </td> <td>{{person.lastname}} </td> <td>{{person.age | number}}</td> <td>{{person.cars}} </td> </tr>
回答3:
Inside your ng-repeat directive you are using a number filter
<td>{{person.id | number}}</td>
Filters are used to format the output, but they don't update the Model properties. For example: person.id = 1234.56789 will be rendered as 1,234.568.
As mentioned above you have to convert age to type Number. Then orderBy will work as it should. For example inside your service:
angular.forEach(People.details, function (detail) { detail.age = parseFloat(detail.age); });
回答4:
if your orderBy value is not a variable pointing to another string but rather the attribute by which you'll be sorting, you must put it in quotes.
person in people | orderBy:'-id'
If, after you've parsed your int or float and are still not sorting correctly, it might be because of this. <:/ (that's my dunce cap)
回答5:
I faced the same problem when I was working with a text field and ngModel to change the value of the model I was ordering by. Because the value was regarded as string.
With new HTML5 <input type="number" />
, Angular parses the value of the input field to a number (float I think), which helped me to get the correct order.
回答6:
Let orderBy
point to a method owned by the scope or its not isolated ancestors and let this method return a number casted from the string. You may have to write a directive inheriting the person
scope created by the ngRepeat instances.. to add this method.
Moreover in your case, the age is a string where it could have been an integer and so the numeric sort would be natively applied.
If you cannot change the data serverside then alter it clientside while fetching.
回答7:
It is best to update your repeating collection whenever a sort function is called. Here I am using Lodash - orderBy just to order the collection based on a sorter function. Lets say the sort is called on click of table column head.
Example:
Collection object:
ctrl.people = [{"id":"42","firstname":"Sarah","lastname":"Dilby","age":"40","cars":"Yaris","salary": 700}, {"firstname":"Jason","lastname":"Diry","age":"5","id":"5","cars":"Lexia","salary": 500},{"id":"6","firstname":"Bilson","lastname":"Berby","age":"1","cars":"Tipo","salary": 400}];
User clicks column head to sort by age:
<th class="col-age" data-ng-click="ctrl.sortColumn('age')">Age</th>
Method called:
ctrl.sortColumn('age'); // where age is column containing numbers only
Method implementation:
ctrl.sortedCol = 'firstname'; //Default sort column ctrl.sortColumn = (column) => { ctrl.sortedCol = column; //age let order = 'asc'; //you can decide the order based on your own logic ctrl.people = _.orderBy(ctrl.people, [ctrl.sorter], [order]); //update the collection }; ctrl.sortColumn(ctrl.sortedCol); //called on initial rendering
Sorter function: Returns the sorted collection based on column type
ctrl.sorter = (item) => { const columnType = ctrl.getColumnType(); if(item[ctrl.sortedCol] && columnType === 'string'){ return item[ctrl.sortedCol].toLowerCase(); } else if(item[ctrl.sortedCol] && columnType === 'number'){ return parseInt(item[ctrl.sortedCol]); } else{ return item[ctrl.sortedCol]; } };
Decide column type: Can be string, number or even date
ctrl.getColumnType = () => { if(ctrl.sortedCol === 'firstname' || ctrl.sortedCol === 'lastname' || ctrl.sortedCol === 'cars'){ return 'string'; } else if(ctrl.sortedCol === 'salary' || ctrl.sortedCol === 'age'){ return 'number'; } };