Подтвердить что ты не робот

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>

Большое спасибо, если вы можете помочь мне понять, почему числовые данные не сортируются как числа и почему они сортируются как текст.

4b9b3361

Ответ 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';
    }
};