Knockout pre-rendered

Downloads in past


7010.11.05 years ago9 years agoMinified + gzip package size for knockout-pre-rendered in KB


Knockout pre-rendered binding handlers
Bower version npm version Build Status Build status Coverage Status This library adds two new binding handlers to Knockout that allow observables to be initialized from pre-rendered HTML content:
  • init: initialize an observable to a value retrieved from existing HTML content.
  • foreachInit: wraps the foreach binding, with the observable array's elements bound to existing HTML elements.

Init binding

Suppose we have the following view model: ```javascript function ViewModel() { = ko.observable(); } ``` Normally, you'd bind the name observable to an HTML element as follows: ```html Michael Jordan ``` Once the binding has been applied however, the text within the <span> element will be cleared, as the bound observable did not have a value (existing HTML content is ignored). We can fix this by specifying the init binding handler before the text binding handler: ```html Michael Jordan ``` Now, the text within the <span> element is left unchanged. This is due to the init binding handler setting the observable's value to the text content of the bound element. As Knockout binding handlers are executed left-to-right, when the text binding executes the init binding will already have initialized the observable. You can combine the init handler with any binding, as long as you ensure that it is listed before the other bindings: ```html Michael Jordan ```


By default, the init binding will set the observable's value to a string. If you want to convert to a different type, you can specify a convert function: ```html 198 ``` Now, the observable's value will be set to what the convert function, with the innerText value as its parameter, returns.

Custom conversion

It is also possible to use your own, custom conversion function. You could, for example, define it in your view model: ```javascript function CustomConvertViewModel() { this.dateOfBirth = ko.observable(); this.parseDate = function(innerText) {
return new Date(Date.parse(innerText));
}; } ``` You can then use this custom convert function as follows: ```html February 17, 1963 ```

Virtual elements

You can also use the init binding handler as a virtual element: ```html Michael Jordan ``` Converting works the same as before: ```html 198 ``` Note that we now need to explicitly specify the field parameter, which points to the observable to initialize. In our previous examples, the init binding was able to infer this due to it being combined with the text, textInput, value or checked binding and using the observable they were pointing to. As a consequence, the following bindings are equivalent: ```html Michael Jordan Michael Jordan ```

Explicit value

If you provide a value parameter to the init binding, that value will be used to initialize the observable instead: ```html Michael Jordan ``` This would result in the observable's value being set to "Larry Bird", and thus the element's content is changed once the text binding is applied.

Multiple values

If you want to initialize multiple observable's at once, you just specify them as key/value pairs: ```html ``` This would set the city observable to "London" and the year observable to 230. Note: the keys cannot be equal to the "value", "convert" or "field" strings.

Supported bindings

The init binding can be used with the following bindings:
  • text: the value is set to the bound element's text contents.
  • textInput: the value is set to the bound element's text contents.
  • value: the value is set to the bound element's value.
  • html: the value is set to the bound element's inner HTML value.
  • attr: the bound attribute properties are set to their respective attribute values.
  • checked: the value is set to the bound element's checked value.
  • visible: the value is set to true or false depending on the bound element's visibility.
  • enable: the value is set to true if the bound element does not have a disabled attribute; otherwise, it is set to false.
  • disable: the value is set to true if the bound element has a disabled attribute; otherwise, it is set to true.

Foreach init binding

This binding handler wraps the foreach binding, but instead of creating new HTML elements, it binds to existing HTML elements. Consider the following view model: ```javascript function PersonViewModel() { = ko.observable(); } function ForeachViewModel() { this.persons = ko.observableArray(); this.persons.push(new PersonViewModel()); this.persons.push(new PersonViewModel()); this.persons.push(new PersonViewModel()); } ``` We can bind the elements in the persons observable array to existing HTML elements by using the foreachInit binding handler: ```html
  • Michael Jordan
  • Larry Bird
  • Magic Johnson
``` There are several things to note:
  1. There must be one child element with the data-template attribute. This element will be used as the template when new items are added to the array.
  1. Elements to be bound to array items must have the data-init attribute.
  1. You can use the init binding handler to initialize the array items themselves.


You can also use a template that is defined elsewhere on the page: ```html
  • Michael Jordan
  • Larry Bird
  • Magic Johnson
``` Note: if you use a named template, the data-init and data-template can be omitted.

Create missing array elements

Up until now, we assumed that the observable array already contained an item for each HTML element bound inside the foreachInit section. However, you can also have the foreachInit binding handler dynamically add an element for each bound element. Take the following view model: ```javascript function ForeachDynamicViewModel() { this.persons = ko.observableArray(); this.addPerson = function() {
this.persons.push(new PersonViewModel());
}; } ``` Note that the persons observable array does not contain any elements, but that it does have a function to add a new element to the array. We can use the foreachInit binding handler as follows: ```html
  • Michael Jordan
  • Larry Bird
  • Magic Johnson
``` What happens is that for each element with the data-init attribute, the function specified in the createElement parameter is called. Our modified code now looks as follows: ```javascript function ForeachDynamicViewModel() { this.persons = ko.observableArray(); this.createPerson = function() {
return new PersonViewModel();
}; } ```

Processing data changes

The foreachInit binding does not immediately process changes. Instead, it queues all changes, which it then later processes all at once. If you want to do additional processing before or after each queue processing round, you can use the dataChanged, beforeQueueFlush and afterQueueFlush attributes: ```html
``` All three attributes point to callback functions with one argument: the change queue being processed. Each change item in this queue has three properties:
  • index: the index of the item in the underlying array.
  • status: indicates the status of the change item, either existing, added or removed.
  • value: the array item being processed.
We can use these callbacks in our view model as follows: ```javascript function ForeachQueueCallbackViewModel() { this.persons = ko.observableArray(); this.dataChanged = function(changes) {
console.log(changes.added.length + " items have been added");
console.log(changes.existing.length + " items were modified");
console.log(changes.deleted.length + " items were deleted");
}; this.beforeQueue = function(changeQueue) {
console.log(changeQueue.length + " queued items will be processed");
}; this.afterQueue = function(changeQueue) {
console.log(changeQueue.length + " queued items have been processed");
}; } ``` There are two main differences between the dataChanged and beforeQueue callbacks:
  1. The dataChanged callback is also called upon initialization, when the input array may be empty.
  1. The dataChanged callback on receives the new changes that will be added to the queue, whereas beforeQueue will receive the complete queue.


The best way to install this library is using Bower:
bower install knockout-pre-rendered
You can also install the library using NPM:
npm install knockout-pre-rendered --save-dev
The library is also available from a CDN.


There is a JSBin demo for each of the binding handlers:


Support multiple bindings on a single element. (by <a href="">revengineering</a>).
Fix for repeating tables. (by <a href="">penguinstampede</a>).
Support plain JS and ko-es5 models. (by <a href="">revengineering</a>).
Added nodesPerElement option. (by <a href="">revengineering</a>)<br/>
Fixed some browser compatibility bugs.
Fix virtual elements children bug.
More efficient setting of initial elements for backing observables.<br/>
Allow computed observables as backing.
<td>Added `dataChanged` callback.</td>
<td>Fixed bug with missing attribute values.</td>
<td>Fixed <tr> in <script> template bug.</td>
<td>Fixed window.document bug.</td>
<td>Added support for `attr`, `html`, `visible`, `enable` and `disable` bindings.</td>
<td>Fixed $index not updating when deleting items.</td>
<td>Fixed missing $index property in foreachInit binding.</td>
Fixed bug where foreachInit did not correctly read template.<br/>
Added links to two more foreachInit demo's.
<td>Added support for `checked` binding handler.</td>
<td>Fixed named template bug.</td>
<td>Added support for initialization through key/value pairs.</td>
<td>Combined textInit, valueInit and init binding handlers into single init binding.</td>
<td>Added support for automatically creating missing array elements.</td>
<td>Added Bower support.</td>
<td>None. Initial version.</td>


Many thanks go out to Brian M Hunt, which fastForEach binding handler formed the basis of the foreachInit binding handler.


Apache License 2.0