diff --git a/README.md b/README.md
index bd87a40..977200e 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,10 @@ It's also possible to sort by nested attibutes by separating the attribute names
Pet name
+Secondary sort is possible by passing the `thenBy` param with comma separated attribute names:
+
+ Pet name
+
See full examples in examples folder.
##Dependencies
diff --git a/examples/then-by-sort.html b/examples/then-by-sort.html
new file mode 100644
index 0000000..2ff3055
--- /dev/null
+++ b/examples/then-by-sort.html
@@ -0,0 +1,43 @@
+
+
+ Basic Example | knockout.bindings.orderable.js
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/knockout.bindings.orderable.js b/knockout.bindings.orderable.js
index 0c6936c..3409bdd 100644
--- a/knockout.bindings.orderable.js
+++ b/knockout.bindings.orderable.js
@@ -15,29 +15,87 @@
return o;
},
+ // Extracts value from a field if its a function or not.
+ getVal: function(object, string){
+ // first getProperty/field out of object
+ var field = ko.bindingHandlers.orderable.getProperty(object, string);
+ // then get the val if its a function or not.
+ return (typeof field === 'function') ? field() : field;
+ },
+
compare: function (left, right) {
- if (typeof left === 'string' || typeof right === 'string') {
- return left ? left.localeCompare(right) : 1;
- }
+
+ left = left == null ? '' : left;
+ right = right == null ? '' : right;
+
+ //if (typeof left === 'string' || typeof right === 'string') {
+ // return left ? left.localeCompare(right) : 1;
+ //}
+
if (left > right)
return 1;
return left < right ? -1 : 0;
},
- sort: function (viewModel, collection, field) {
+ //get all sort results of thenBy fields
+ sortThenBy: function(left, right, field, thenBy, orderDirection){
+ var sortResults = [];
+
+ if(!thenBy) return sortResults;
+
+ var thenByFields = thenBy.split(','); // extract fields
+ //console.log('sortResults', thenByFields, left, right, field);
+
+ for (var i = 0; i < thenByFields.length; i++) {
+
+ var tbField = thenByFields[i].trim();
+ var lv = ko.bindingHandlers.orderable.getVal(left, tbField);
+ var rv = ko.bindingHandlers.orderable.getVal(right, tbField);
+ var sort = 0;
+
+ if(orderDirection == "desc") {
+ sort = ko.bindingHandlers.orderable.compare(rv, lv);
+ } else {
+ sort = ko.bindingHandlers.orderable.compare(lv, rv);
+ }
+
+ //console.log('sortResults', lv, rv, sort);
+ sortResults.push(sort);
+ }
+
+ return sortResults;
+ },
+
+ sort: function (viewModel, collection, field, thenBy) {
+ var orderDirection = viewModel[collection].orderDirection();
+
//make sure we sort only once and not for every binding set on table header
if (viewModel[collection].orderField() == field) {
viewModel[collection].sort(function (left, right) {
- var left_field = ko.bindingHandlers.orderable.getProperty(left, field);
- var right_field = ko.bindingHandlers.orderable.getProperty(right, field);
- var left_val = (typeof left_field === 'function') ? left_field() : left_field;
- right_val = (typeof right_field === 'function') ? right_field() : right_field;
- if (viewModel[collection].orderDirection() == "desc") {
- return ko.bindingHandlers.orderable.compare(right_val, left_val);
+ var leftVal = ko.bindingHandlers.orderable.getVal(left, field);
+ var rightVal = ko.bindingHandlers.orderable.getVal(right, field);
+
+ // these will hold all fields for the thenBy fields
+ // evaluate all thenBy compare first
+ var thenByResults = ko.bindingHandlers.orderable.sortThenBy(left, right, field, thenBy, orderDirection);
+
+ var sort = 0;
+
+ if (orderDirection == "desc") {
+ sort = ko.bindingHandlers.orderable.compare(rightVal, leftVal);
} else {
- return ko.bindingHandlers.orderable.compare(left_val, right_val);
+ sort = ko.bindingHandlers.orderable.compare(leftVal, rightVal);
+ }
+
+ // sort then by fields in same order
+ if(thenByResults.length > 0){
+ for (var i = 0; i < thenByResults.length; i++) {
+ sort = sort || thenByResults[i];
+ }
}
+
+ return sort;
});
}
},
@@ -46,21 +104,27 @@
//get provided options
var collection = valueAccessor().collection;
var field = valueAccessor().field;
+ var thenBy = valueAccessor().thenBy;
- //add a few observables to ViewModel to track order field and direction
+ //add a few observables to ViewModel to track order field, direction, and then by fields
if (viewModel[collection].orderField == undefined) {
viewModel[collection].orderField = ko.observable();
}
if (viewModel[collection].orderDirection == undefined) {
viewModel[collection].orderDirection = ko.observable("asc");
}
+ if (viewModel[collection].orderThenByFields == undefined) {
+ viewModel[collection].orderThenByFields = ko.observable();
+ }
var defaultField = valueAccessor().defaultField;
var defaultDirection = valueAccessor().defaultDirection || "asc";
+ var defaultThenBy = valueAccessor().defaultThenBy || null;
if (defaultField) {
viewModel[collection].orderField(field);
viewModel[collection].orderDirection(defaultDirection);
- ko.bindingHandlers.orderable.sort(viewModel, collection, field);
+ viewModel[collection].orderThenByFields(defaultThenBy);
+ ko.bindingHandlers.orderable.sort(viewModel, collection, field, thenBy);
}
//set order observables on table header click
@@ -77,14 +141,15 @@
}
viewModel[collection].orderField(field);
+ viewModel[collection].orderThenByFields(thenBy);
});
//order records when observables changes, so ordering can be changed programmatically
viewModel[collection].orderField.subscribe(function () {
- ko.bindingHandlers.orderable.sort(viewModel, collection, field);
+ ko.bindingHandlers.orderable.sort(viewModel, collection, field, thenBy);
});
viewModel[collection].orderDirection.subscribe(function () {
- ko.bindingHandlers.orderable.sort(viewModel, collection, field);
+ ko.bindingHandlers.orderable.sort(viewModel, collection, field, thenBy);
});
},