Angular orderBy сортировка числа как текст в ng-repeat
У меня есть эти данные:
[{"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"}]
Когда я orderBy id или по возрасту в ng-repeat, он сортирует это число как текст. Поскольку я не могу найти, что это написано, что это проблема в любом месте, я предполагаю, что есть проблема с моим кодом. Я создал эту скрипту: http://jsfiddle.net/vsbGH/1/ Извините за шаблон, но jsfiddle не разрешает в поле html. В любом случае, это код, который загружает и сортирует данные:
//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;
}
}
И это часть ng-repeat шаблона:
<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>
Большое спасибо, если вы можете помочь мне понять, почему числовые данные не сортируются как числа и почему они сортируются как текст.
Ответы
Ответ 1
Я думаю, что наиболее подходящим решением является правильное форматирование чисел, которые у меня есть на объектах JSON, т.е. не для их переноса в кавычки. Итак:
[{"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"}]
становится:
[{"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"}]
Я предполагаю, что решение SergL хорошо, если невозможно изменить формат данных JSON.
Чтобы добавить к этому, проблема в моем конкретном случае связана с функцией PHP json_encode на стороне сервера. По умолчанию он обрабатывает числа как строки. Чтобы исправить, мне пришлось добавить параметр JSON_NUMERIC_CHECK
в метод кодирования в PHP скрипт:
json_encode($assoc_array,JSON_NUMERIC_CHECK);
Ответ 2
Вам не нужно изменять свой JSON. Вы можете передать функцию orderBy, например, следующим образом:
$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
Внутри вашей директивы ng-repeat вы используете числовой фильтр
<td>{{person.id | number}}</td>
Фильтры используются для форматирования вывода, но они не обновляют свойства модели. Например: person.id = 1234.56789 будет отображаться как 1 234.568.
Как уже упоминалось выше, вы должны преобразовать возраст в тип Number. Тогда orderBy будет работать так, как должен.
Например, внутри вашей службы:
angular.forEach(People.details, function (detail) {
detail.age = parseFloat(detail.age);
});
Ответ 4
если ваше значение orderBy не является переменной, указывающей на другую строку, а скорее атрибутом, по которому вы будете сортировать, вы должны поместить его в кавычки.
person in people | orderBy:'-id'
Если после того, как вы проанализировали свой int или float и по-прежнему не отсортированы правильно, возможно, из-за этого. <:/(что мой колпачок)
Ответ 5
У меня возникла та же проблема, когда я работал с текстовым полем и ngModel, чтобы изменить значение модели, которую я заказывал. Поскольку значение считалось строкой.
С новым HTML5 <input type="number" />
, Angular анализирует значение поля ввода на число (по-моему, я думаю), что помогло я должен получить правильный порядок.
Ответ 6
Пусть orderBy
указывает на метод, принадлежащий области действия или ее неизолированным предкам, и пусть этот метод возвращает число, отлитое из строки.
Возможно, вам придется написать директиву, наследующую область person
, созданную экземплярами ngRepeat.., чтобы добавить этот метод.
Кроме того, в вашем случае возраст представляет собой строку, где он мог бы быть целым числом, и поэтому числовая сортировка была бы применена.
Если вы не можете изменить серверы данных, тогда измените его clientide при извлечении.
Ответ 7
Лучше всего обновлять повторяющуюся коллекцию всякий раз, когда вызывается функция сортировки. Здесь я использую Lodash - orderBy, чтобы заказать коллекцию на основе функции сортировщика. Допустим, что сортировка называется щелчком заголовка столбца таблицы.
Пример:
Объект коллекции:
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}];
Пользователь нажимает заголовок столбца для сортировки по возрасту:
<th class="col-age" data-ng-click="ctrl.sortColumn('age')">Age</th>
Вызывается метод:
ctrl.sortColumn('age'); // where age is column containing numbers only
Реализация метода:
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
Функция сортировщика: возвращает отсортированную коллекцию на основе типа столбца
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];
}
};
Определить тип столбца: может быть строка, число или дата
ctrl.getColumnType = () => {
if(ctrl.sortedCol === 'firstname' || ctrl.sortedCol === 'lastname' || ctrl.sortedCol === 'cars'){
return 'string';
} else if(ctrl.sortedCol === 'salary' || ctrl.sortedCol === 'age'){
return 'number';
}
};