From 546eba03c96a88ae964631ea0834ed167924f624 Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Mon, 15 Feb 2016 16:19:43 -0400 Subject: [PATCH 1/3] Add "then by" sort parameter binding. #14 --- README.md | 4 ++ examples/then-by-sort.html | 43 +++++++++++++++++ knockout.bindings.orderable.js | 84 +++++++++++++++++++++++++++++----- 3 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 examples/then-by-sort.html 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 + + + + + + + + + + + + + + + + + + + + + + + +
First NameLast NameAge
+ + \ No newline at end of file diff --git a/knockout.bindings.orderable.js b/knockout.bindings.orderable.js index 0c6936c..ae855a0 100644 --- a/knockout.bindings.orderable.js +++ b/knockout.bindings.orderable.js @@ -15,6 +15,14 @@ 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; @@ -25,19 +33,64 @@ 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 +99,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 +136,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); }); }, From 58422a4a142bf5cda781a1a4cc0762e1379e4dc1 Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Mon, 15 Feb 2016 16:38:06 -0400 Subject: [PATCH 2/3] Remove console.log --- knockout.bindings.orderable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knockout.bindings.orderable.js b/knockout.bindings.orderable.js index ae855a0..60ed137 100644 --- a/knockout.bindings.orderable.js +++ b/knockout.bindings.orderable.js @@ -40,7 +40,7 @@ if(!thenBy) return sortResults; var thenByFields = thenBy.split(','); // extract fields - console.log('sortResults', thenByFields, left, right, field); + //console.log('sortResults', thenByFields, left, right, field); for (var i = 0; i < thenByFields.length; i++) { From 353ee51d32c96980d303df15a02caf6813f5e26f Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Tue, 1 Mar 2016 13:38:20 -0400 Subject: [PATCH 3/3] Don't compare with localCompare as it brings undesired results. --- knockout.bindings.orderable.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/knockout.bindings.orderable.js b/knockout.bindings.orderable.js index 60ed137..3409bdd 100644 --- a/knockout.bindings.orderable.js +++ b/knockout.bindings.orderable.js @@ -24,9 +24,14 @@ }, 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;