Occasionally, you may find opportunities to streamline your code by attaching new functionality to Knockout’s core value types. You can define custom functions on any of the following types:
Because of inheritance, if you attach a function to ko.subscribable
, it will be available on all the others too. If you attach a function to ko.observable
, it will be inherited by ko.observableArray
but not by ko.computed
.
To attach a custom function, add it to one of the following extensibility points:
ko.subscribable.fn
ko.observable.fn
ko.observableArray.fn
ko.computed.fn
Then, your custom function will become available on all values of that type created from that point onwards.
Note: It’s best to use this extensibility point only for custom functions that are truly applicable in a wide range of scenarios. You don’t need to add a custom function to these namespaces if you’re only planning to use it once.
Example: A filtered view of an observable array
Here’s a way to define a filterByProperty
function that will become available on all subsequently-created ko.observableArray
instances:
1 2 3 4 5 6 7 8 9 10 11 | ko.observableArray.fn.filterByProperty = function (propName, matchValue) { return ko.pureComputed( function () { var allItems = this (), matchingItems = []; for ( var i = 0; i < allItems.length; i++) { var current = allItems[i]; if (ko.unwrap(current[propName]) === matchValue) matchingItems.push(current); } return matchingItems; }, this ); } |
This returns a new computed value that provides a filtered view of the array, while leaving the original array unchanged. Because the filtered array is a computed observable, it will be re-evaluated whenever the underlying array changes.
The following live example shows how you could use this:
Source code: View
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | < h3 >All tasks (< span data-bind = "text: tasks().length" > </ span >)</ h3 > < ul data-bind = "foreach: tasks" > < li > < label > < input type = "checkbox" data-bind = "checked: done" /> < span data-bind = "text: title" > </ span > </ label > </ li > </ ul > < h3 >Done tasks (< span data-bind = "text: doneTasks().length" > </ span >)</ h3 > < ul data-bind = "foreach: doneTasks" > < li data-bind = "text: title" ></ li > </ ul > |
Source code: View model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function Task(title, done) { this .title = ko.observable(title); this .done = ko.observable(done); } function AppViewModel() { this .tasks = ko.observableArray([ new Task( 'Find new desktop background' , true ), new Task( 'Put shiny stickers on laptop' , false ), new Task( 'Request more reggae music in the office' , true ) ]); // Here's where we use the custom function this .doneTasks = this .tasks.filterByProperty( "done" , true ); } ko.applyBindings( new AppViewModel()); |
It’s not mandatory
If you tend to filter observable arrays a lot, adding a filterByProperty
globally to all observable arrays might make your code tidier. But if you only need to filter occasionally, you could instead choose not to attach to ko.observableArray.fn
, and instead just construct doneTasks
by hand as follows:
1 2 3 4 5 6 7 | this .doneTasks = ko.pureComputed( function () { var all = this .tasks(), done = []; for ( var i = 0; i < all.length; i++) if (all[i].done()) done.push(all[i]); return done; }, this ); |
Please login to continue.