Объект сортировки AngularJs в ngRepeat
Я использую AngularJs и обнаружил проблему с упорядочением свойств хэш-объекта в шаблоне.
Мой объект похож:
function TestCtrl($scope){
$scope.week = {'MONDAY': ['manuel'], 'TUESDAY': [], 'WEDNESDAY': ['valerio'], 'THURSDAY': ['manuel', 'valerio'], 'FRIDAY': []}
}
Теперь, когда я пытаюсь напечатать эти значения в моем шаблоне:
<div ng-repeat="(day, names) in week">
<span>{{day}}</span>
<ul> <li ng-repeat="name in names">{{name}}</li> </ul>
</div>
Порядок напечатанных дней отличается: FRIDAY MONDAY THURSDAY TUESDAY WEDNESDAY
Я попытался применить фильтр orderBy
, но я думаю, что он не работает с объектами, а просто с массивами...
Как я могу его заказать?
Ответы
Ответ 1
Согласно AngularJS docs (версия 1.3.20):
Вы должны знать, что спецификация JavaScript не определяет в каком порядке он вернет ключи для объекта. Чтобы иметь гарантированный детерминированный порядок для ключей, версии Angular до и включая 1.3 сортировать клавиши в алфавитном порядке.
Обходной путь заключается в использовании массива ключей:
function TestCtrl($scope){
$scope.week = {
'MONDAY': ['manuel'], 'TUESDAY': [],
'WEDNESDAY': ['valerio'], 'THURSDAY': ['manuel', 'valerio'],
'FRIDAY': []}
$scope.weekDays = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"];
}
Используйте массив для просмотра итерации:
<div ng-repeat="day in weekDays">
<span>{{day}}</span>
<ul> <li ng-repeat="name in week[day]">{{name}}</li> </ul>
</div>
Обновление от версии AngularJS 1.4.6 docs:
Версия 1.4 удалила сортировку по алфавиту. Теперь мы полагаемся на заказ возвращенный браузером при запуске for key in myObj
.
Ответ 2
Невозможно заказать хеш-объекты, подобные этому. Не только в angular, но и в javascript вообще.
Я бы преобразовал хэш-объект в массив объектов, что-то вроде этого:
$scope.week = [{day: 'MONDAY', names: ['manuel']}, {day: 'TUESDAY', names: []} ...];
И затем измените представление на что-то подобное:
<div ng-repeat="day in week|orderBy:'day'">
<span>{{day.day}}</span>
<ul> <li ng-repeat="name in day.names">{{name}}</li> </ul>
</div>
Ответ 3
Это было исправлено в Angular 1.4. Как указано в официальной документации Angular ниже:
Версия 1.4 удалила сортировку по алфавиту. Теперь мы полагаемся на заказ возвращенный браузером при запуске for key in myObj
https://docs.angularjs.org/api/ng/directive/ngRepeat
Ответ 4
На самом деле существует простое решение...
Клавиши объекта не упорядочены по умолчанию НО, если вы создаете объект в браузере с нуля, ваш браузер WILL знает порядок;)
Пример:
// test-1
var data = {};
data['a'] = 10;
data['b'] = 5;
data['c'] = 2;
Object.keys(data); // ["a", "b", "c"]
// test-2
var data = {};
data['b'] = 5;
data['a'] = 10;
data['c'] = 2;
Object.keys(data); // ["b", "a", "c"]
Так просто... воссоздайте объект... или используйте этот простой фильтр:
.filter('orderObject', function () {
return function (object, reverse) {
var keys = Object.keys(object || {}).sort();
if (reverse) keys.reverse();
for (var ordered = {}, i = 0; keys[i]; i++) {
ordered[keys[i]] = object[keys[i]];
}
return ordered;
}
})
Пример с регулярными объектами:
<!-- MARKUP : DEFAULT -->
<table>
<tr ng-repeat="(key, value) in data">
<td>{{key}}</td>
<td>{{value}}</td>
</tr>
</table>
<!-- RESULT : test-1 -->
<table>
<tr>
<td>a</td>
<td>10</td>
</tr>
<tr>
<td>b</td>
<td>5</td>
</tr>
<tr>
<td>c</td>
<td>2</td>
</tr>
</table>
<!-- RESULT : test-2 -->
<table>
<tr>
<td>b</td>
<td>5</td>
</tr>
<tr>
<td>a</td>
<td>10</td>
</tr>
<tr>
<td>c</td>
<td>2</td>
</tr>
</table>
Пример с отсортированными объектами:
<!-- MARKUP : with FILTER orderObject:<reverse?> -->
<table>
<tr ng-repeat="(key, value) in data | orderObject">
<td>{{key}}</td>
<td>{{value}}</td>
</tr>
</table>
<!-- RESULT : test-1 without reverse -->
<table>
<tr>
<td>a</td>
<td>10</td>
</tr>
<tr>
<td>b</td>
<td>5</td>
</tr>
<tr>
<td>c</td>
<td>2</td>
</tr>
</table>
<!-- RESULT : test-2 with reverse -->
<table>
<tr>
<td>c</td>
<td>2</td>
</tr>
<tr>
<td>b</td>
<td>5</td>
</tr>
<tr>
<td>a</td>
<td>10</td>
</tr>
</table>
Ответ 5
Этот вопрос старый, но в итоге я получил ответ на этот вопрос, который, как я думал, может быть улучшением некоторых предыдущих ответов.
Вместо того, чтобы просто преобразовать объект в массив, его намного больше DRY для создания фильтра angular, который делает это для вас, а затем ngRepeat
или ngOptions
.
В качестве примера:
angular.module('myproject')
.filter('objOrder', function () {
return function(object) {
var array = [];
angular.forEach(object, function (value, key) {
array.push({key: key, value: value});
});
return array;
};
});
Затем с объектом вроде:
$scope.degrees: {
ASC: "Associate's",
BAS: "Bachelor's",
MAS: "Master's",
MD: "M.D.",
JD: "J.D.",
PHD: "Ph.D",
OTH: "Other"
}
Мы могли бы использовать его так:
<select
ng-model="myDegree"
required
ng-options="item.key as item.value for item in degrees | objOrder"
>
</select>
Таким образом, вам не нужно создавать новый массив и загрязнять $scope
, и вам не нужно возвращаться и изменять фактический объект degrees
, который может иметь нежелательные побочные эффекты.