knockout-classBindingProvider is a Knockout.js plugin that changes the way that Knockout discovers and parses bindings on elements. With this library, Knockout will look for a data-class
attribute (by default) and use its value to key into a JavaScript object containing definitions for each binding. This lets you move your bindings specifications out of the markup and into a layer of JavaScript code.
This library uses the bindingProvider
extensibility point found in Knockout 2.0+. A description of this functionality and the origin of this implementation can be found in this blog post.
- The markup can stay clean and simple
- Bindings can be re-used, even at different scopes
- You can set breakpoints in the bindings to inspect the data being passed through them
- You can do logging in the bindings to understand how many times they are being called
- You can change/alter the bindings on an element whenever your bindings are triggered
- Bindings go through less parsing (do not need to go from an object literal in a string to code)
To get started, reference the knockout-classBindingProvider.min.js
script after Knockout.js
In your code, prior to calling ko.applyBindings
, tell Knockout that you want to use a new binding provider.
//bindings - a JavaScript object containing binding definitions
//options - an object that can contain these properties:
// attribute - override the attribute used for bindings (defaults to `data-class`)
// virtualAttribute - override the text used for virtual bindings (defaults to `class` and specified as `ko class:`)
// bindingRouter - custom function for routing class names to the appropriate binding
// fallback - look for normal `data-bind` bindings after failing with this provider on an element (defaults to false)
ko.bindingProvider.instance = new ko.classBindingProvider(bindings, options);
If you are using an AMD loader, then the classBindingProvider is exported rather than placed on the ko
object, so the initialization would look like:
require(["knockout", "knockout-classBindingProvider"], function(ko, ClassBindingProvider) {
var bindings, options;
//setup bindings and options
ko.bindingProvider.instance = new ClassBindingProvider(bindings, options);
});
Sample bindings object:
var bindings = {
title: function(context, classes) {
return {
value: this.title,
enable: context.$parent.editable
}
},
input: {
valueUpdate: 'afterkeydown'
},
list: {
items: function(context, classes) {
return {
foreach: this.items
}
}
}
};
In the example, the title
binding class uses a function that is given the binding context and an array containing all of the binding classes listed on the element. The value of this
is also set to the current data (context.$data
) to make it easy to access its properties. The function needs to return the bindings that should be used for the element. Alternatively, you can specify a static value like in the input
binding class where the resulting bindings can be determined at design time, without the need for the current context.
Then, you would use these bindings like:
<input data-class="title input" />
You can also use it in a virtual binding like:
<!-- ko class: list.items -->
<div data-class="name"></div>
<!-- /ko -->
Similar to CSS classes, you can list multiple keys and the resulting bindings will be combined for the element. By default, a binding class can follow an object tree by writing the property path separated by periods. Using the bindings object above, you can do:
<ul data-class="list.items">
<li> ... </li>
</ul>
Also, when using a function for a binding class, the second argument passed to the function will be an array containing all of the binding classes listed on the element. These classes can even be treated as modifiers or dynamic values when generating the bindings, as each binding class does not have to actually exist in the bindings object.
At run-time, you can also access the bindings, by using ko.bindingProvider.instance.bindings
. This allows you to add and remove bindings as your application needs them. You can also merge a new set of bindings into the existing bindings using ko.bindingProvider.instance.registerBindings(newBindings);
.
To use your own binding router, set options.bindingRouter = function(class, bindings){...}
. class
is the current class being requested and bindings
is provider's current bindings object. Be sure to return a valid binding object.
- Knockout 2.0+
This project uses grunt for building/minifying.
The examples folder has small samples for normal and AMD usage. Here is the non-AMD sample in jsFiddle: http://jsfiddle.net/rniemeyer/LvwRt/.