Reputation: 4429
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.
Upvotes: 33
Views: 75490
Reputation: 1502
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';
}
};
Upvotes: -1
Reputation: 4578
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>
Upvotes: 31
Reputation: 51
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)
Upvotes: 5
Reputation: 7233
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.
Upvotes: 3
Reputation: 4429
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);
Upvotes: 52
Reputation: 680
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);
});
Upvotes: 15
Reputation: 2434
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.
Upvotes: 0