knockout.clear
v0.1.2
Published
Minimal utilities to make it easy to get KnockoutJS to clear up garbage automatically
Downloads
4
Readme
knockout.clear
Minimal utilities to make it easy to get KnockoutJS to clear up garbage automatically
Motivation
In brief, with the introduction of ko.pureComputed
it should not be necessary to manually dispose of anything in Knockout:
- Only use
ko.pureComputed
and when your observables are not subscribed to, they will not subscribe to anything else and so will not be kept alive unnecessarily. - Well-behaved bindings will call
ko.cleanNode
on DOM elements before they are discarded, which will make them unsubscribe from your observables.
The only hitch is the need to trigger side-effects that don't return values. A pureComputed
won't do this unless it is awake
, and to be awake it needs to be subscribed to.
One major application of side-effects is the need to make asynchronously-obtained values (such as promises) appear like ordinary observables.
This library defines the building blocks for dealing with side-effects within the framework of pureComputed
, and builds on the resulting pattern to provide an easy way to consume promises.
Usage
All you need is:
- Knockout 3.3 or later
- The single file lib/knockout.clear.js from the knockout.clear repository.
The other stuff in the repository is mostly unit tests.
If you're into bower:
bower install danielearwicker/knockout.clear
Note that this library assumes ko
is a global so it can extend it. If there's a more flexible way to arrange it, I'm open to suggestions.
If you're into TypeScript (entirely optional), you'll also want:
The execute
binding
The execute
binding is useful when you want to create a ko.pureComputed
that acts as a side-effecting function that re-executes when its dependencies change, but doesn't return a value and would not normally be depended on by anything else.
To keep it awake, first you add it to your view model:
var viewModel = {
pointlessLogging: ko.pureComputed(function() {
console.log("Phone number is: " + phoneNumber());
})
};
Then you bind to it in your HTML:
<!-- ko execute: pointlessLogging --><!-- /ko -->
That's it. When you have a simple viewModel/HTML scenario, this is the easy way to keep your pureComputed
re-executing as long as the view is on the page.
For more advanced situations, you might need the API.
API
Note: The TypeScript declarations are presented here as a precise reference, but this library is not actually written in TypeScript and does not depend on it. Code samples are both valid JavaScript and TypeScript.
ko.execute
execute<T>(
pureComputed: KnockoutComputed<any>,
evaluatorFunction: () => T,
thisObj?: any): KnockoutComputed<T>;
ko.execute
is used as an alternative to the execute
binding described above, in situations where you can't (or don't want to) simply modify the HTML bindings.
This is typically in situations where you're building a reusable fragment of a model containing observable data, but you don't want to force your users to use the execute
binding. See ko.unpromise
in this library for an example.
To control when the evaluatorFunction
should be actively re-executing, you must supply a pureComputed
as the first argument (an Error
is thrown otherwise).
The evaluatorFunction
typically returns void
, though it doesn't have to. It must not depend on the pureComputed
that you supplied as the first argument (ideally an error would be thrown if this condition is violated but there doesn't appear to be a way to do that currently).
ko.execute(lastName, function() {
console.log("Phone number is: " + phoneNumber());
});
The returned object is a pureComputed
, on which you can call the extend
function if necessary. Otherwise, you don't need to keep a reference to it and you don't need to manually dispose it.
ko.unpromise
unpromise<T>(evaluatorFunction: () => T | KnockoutExecuteThenable<T>,
options?: {
initialValue?: T;
errorValue?: T;
thisArg?: any;
}): KnockoutComputed<T>;
ko.unpromise
creates a pureComputed
that automatically converts promises (or indeed anything vaguely promise-like) into plain values.
The evaluatorFunction
will likely depend on other observables which act as parameters to some API that returns a promise, e.g. using jQuery if you like:
var text = ko.unpromise(function() {
return $.get('page/' + selectedPageName());
});
The value of text
will initially be undefined
, but a short time later the asynchronous $.get
will complete and text
will change its value to the returned data. It acts just like any other pureComputed
so you can bind to it or read it from other pureComputed
s.
You can change the default values by passing options:
var text = ko.unpromise(function() {
return $.get("messages/" + selectedMessage());
}, {
initialValue: "Please wait...",
errorValue: "Message could not be loaded"
});
If errorValue
is not specified, initialValue
is used.
ko.isPureComputed
isPureComputed(obs: any): boolean;
Returns true if the argument was created with ko.pureComputed
.
Note: It is possible to create a pureComputed
by calling ko.computed
and passing pure: true
, but this function cannot detect that. Consequently you cannot use such objects as the the first argument to ko.execute
.
There is a plausible excuse for this: strive to only use ko.pureComputed
, so that you can easily search your code for any remaining uses of ko.computed
and regard them as suspicious.
Running the tests
Install node and grunt-cli. Clone this repository and install it:
git clone http://github.com/danielearwicker/knockout.clear.git
cd knockout.clear
npm install
That will run the command-line tests. There is a further minor test for the execute
binding that you can run by opening the runner page in your browser:
spec/runner.html
Note that all the Knockout 3.3 tests will also run! This is important because knockout.clear
patches and wraps a couple of things (see the source for details).
Acknowledgements
Many thanks to Michael Best for invaluable pointers.