Reputation: 10479
I am trying to add default sorting to an observable array. However, I don't want it to apply to the underlying observable. Just return the array as sorted without affecting the extended target.
I have this extender:
ko.extenders.defaultSort = function (target, sortExpression) {
var expression = sortExpression;
//it is an observable, the underlying value is an array, and it's not a computed observable
if (ko.isObservable(target) && Array.isArray(target()) && ko.isComputed(target) == false) {
//returns the sorted array and sorts the target,
//do not want this behavior, just want to return the underlying array sorted
target.sorted = function () {
var arrayValue = target();
return arrayValue.sort(expression);
};
}
}
I can take this model:
var testModel = ko.validatedObservable({
Confirmations: ko.observableArray([
{
Id: 5,
IsOwner: ko.observable(false),
User: ko.observable("Johnny"),
AvailableLabs: ko.observableArray([
{
Key: "guid", Value: "Something"
},
{
Key: "guid2", Value: "Something2"
},
{
Key: "guid3", Value: "Something3"
}
])
},
{
Id: 2,
IsOwner: ko.observable(true),
User: ko.observable("Same"),
AvailableLabs: ko.observableArray([
{
Key: "guid31", Value: "this is cool"
},
{
Key: "guid15", Value: "another item"
}
])
}
]).extend({
defaultSort: function(a, b){
if(a.Id < b.Id) { return -1;}
if(a.Id > b.Id) { return 1; }
return 0;
}
})
});
If I load the page and view testModel().Confirmations()
normally. I see the array with the ID 5 record being first.
Then if I type:
testModel().Confirmations.sorted()
It returns an array with the ID 2 record first, so it has been sorted now, great! But when I once again type testModel().Confirmations()
, the ID 2 record is now first. I don't want the behavior. I just want to return a separately ordered array. I thought by using target()
to first get the underlying value, then return that, it wouldn't affect the observable, but that appears to be wrong. How can I do that?
Note, I cannot use ko.toJS
or ko.toJSON
because I need to preserve any other nested observables and their extended properties for further processing after binding.
Upvotes: 0
Views: 47
Reputation: 10479
I was able to figure this out, you need to slice the value as a new array first, which knockout provides a helper function to do so straight off the observable:
target.sorted = function () {
return target.slice().sort(expression);
}
The sort is also a knockout wrapped method of the regular sort. Which I found documentation of here:
https://knockoutjs.com/documentation/observableArrays.html
They also did exactly what I was trying to do, have a .sorted
method which returns a copy of the array sorted and doesn't not affect the original. But upon reading this:
https://github.com/knockout/knockout/issues/2501
.sorted
is only in v3.5+, which I don't have.
Upvotes: 0