I have a set of tiles that display a certain number depending on which option is selected by the user. I would now like to implement a sort by whatever number is shown.
The code below shows how I've implemented it (by gettting/setting a value in the parent cards scope). Now, because the orderBy function takes a string, I tried to set a variable in the card scope called curOptionValue and sort by that, but it doesn't seem to work.
So the question becomes, how to I create a custom sort function?
<div ng-controller="aggViewport" >
<div class="btn-group" >
<button ng-click="setOption(opt.name)" ng-repeat="opt in optList" class="btn active">{{opt.name}}</button>
</div>
<div id="container" iso-grid width="500px" height="500px">
<div ng-repeat="card in cards" class="item {{card.class}}" ng-controller="aggCardController">
<table width="100%">
<tr>
<td align="center">
<h4>{{card.name}}</h4>
</td>
</tr>
<tr>
<td align="center"><h2>{{getOption()}}</h2></td>
</tr>
</table>
</div>
</div>
and controller :
module.controller('aggViewport',['$scope','$location',function($scope,$location) {
$scope.cards = [
{name: card1, values: {opt1: 9, opt2: 10}},
{name: card1, values: {opt1: 9, opt2: 10}}
];
$scope.option = "opt1";
$scope.setOption = function(val){
$scope.option = val;
}
}]);
module.controller('aggCardController',['$scope',function($scope){
$scope.getOption = function(){
return $scope.card.values[$scope.option];
}
}]);
Actually the orderBy
filter can take as a parameter not only a string but also a function. From the orderBy
documentation: http://docs.angularjs.org/api/ng.filter:orderBy):
function: Getter function. The result of this function will be sorted using the <, =, > operator.
So, you could write your own function. For example, if you would like to compare cards based on a sum of opt1 and opt2 (I'm making this up, the point is that you can have any arbitrary function) you would write in your controller:
$scope.myValueFunction = function(card) {
return card.values.opt1 + card.values.opt2;
};
and then, in your template:
ng-repeat="card in cards | orderBy:myValueFunction"
The other thing worth noting is that orderBy
is just one example of AngularJS filters so if you need a very specific ordering behaviour you could write your own filter (although orderBy
should be enough for most uses cases).
The accepted solution only works on arrays, but not objects or associative arrays. Unfortunately, since Angular depends on the JavaScript implementation of array enumeration, the order of object properties cannot be consistently controlled. Some browsers may iterate through object properties lexicographically, but this cannot be guaranteed.
e.g. Given the following assignment:
$scope.cards = {
"card2": {
values: {
opt1: 9,
opt2: 12
}
},
"card1": {
values: {
opt1: 9,
opt2: 11
}
}
};
and the directive <ul ng-repeat="(key, card) in cards | orderBy:myValueFunction">
, ng-repeat may iterate over "card1" prior to "card2", regardless of sort order.
To workaround this, we can create a custom filter to convert the object to an array, and then apply a custom sort function before returning the collection.
myApp.filter('orderByValue', function () {
// custom value function for sorting
function myValueFunction(card) {
return card.values.opt1 + card.values.opt2;
}
return function (obj) {
var array = [];
Object.keys(obj).forEach(function (key) {
// inject key into each object so we can refer to it from the template
obj[key].name = key;
array.push(obj[key]);
});
// apply a custom sorting function
array.sort(function (a, b) {
return myValueFunction(b) - myValueFunction(a);
});
return array;
};
});
We cannot iterate over (key, value) pairings in conjunction with custom filters (since the keys for arrays are numerical indexes), so the template should be updated to reference the injected key names.
<ul ng-repeat="card in cards | orderByValue">
<li>{{card.name}} {{value(card)}}</li>
</ul>
Here is a working fiddle utilizing a custom filter on an associative array: http://jsfiddle.net/av1mLpqx/1/
Reference: https://github.com/angular/angular.js/issues/1286#issuecomment-22193332
The following link explains filters in Angular extremely well. It shows how we can define a custom sort logic within an ng-repeat. http://toddmotto.com/everything-about-custom-filters-in-angular-js/
For sorting object with properties, this is the code I have used: (Note that this sort is the standard JavaScript sort method and not specific to angular) Column Name is the name of the property on which sorting is to be performed.
self.myArray.sort(function(itemA, itemB) {
if (self.sortOrder === "ASC") {
return itemA[columnName] > itemB[columnName];
} else {
return itemA[columnName] < itemB[columnName];
}
});