The power of MVVM, the flexibility of JavaScript, the speed and ease of JsRender templates and jQuery
JsViews builds on top of JsRender templates, and adds data-binding and observable data, to provide a fully-fledged MVVM platform for easily creating interactive data-driven single-page apps and websites.
Documentation, downloads, samples and API docs and tutorials are available on the www.jsviews.com website.
The content of this ReadMe is available also as a JsViews Quickstart.
jsviews.js is available from downloads on the jsviews.com site.
CDN delivery is available from the cdnjs CDN at cdnjs.com/libraries/jsviews.
Alternatively:
- It can be installed with Bower, using
$ bower install jsviews
- It can be loaded using an AMD script loader, such as RequireJS
- For installation using Node.js (npm), and loading using Browserify or webpack, see JsViews as a Browserify module and JsViews as a webpack module
(Note that jsviews.js includes all of jsrender.js code -- so jsrender.js does not need to be loaded first.)
JsRender is used for data-driven rendering of templates to strings, ready for insertion in the DOM. (See JsRender Quickstart and JsRender GitHub repository).
JsViews incorporates JsRender templates, together with data-binding, observable data and MVVM support. It provides a powerful platform for building dynamic interactive websites and single-page apps.
(Note: JsRender and JsViews together provide the next-generation implementation of the official jQuery plugins JQuery Templates, and JQuery Data Link -- and supersede those libraries.)
JsViews provides data-linking - so that JsRender templates become data-bound:
- Data-linked tags or elements in your templates will update automatically whenever the underlying data changes.
- Some data-linked tags or elements provide two-way data-linking, so that user interactions will trigger "observable" changes to the underlying data (which may then trigger other updates elsewhere in your templated UI).
Data-linked template tags:
Any JsRender tag, {{...}}
can be data-linked by writing {^{...}}
, as in:
<ul>
{^{for people}} <!--List will update when people array changes-->
<li>{^{:name}}</li> <!--Will update when name property changes-->
{{/for}}
</ul>
Data-linked HTML elements:
HTML elements within templates can be data-linked by adding a data-link
attribute:
<input data-link="name"/> <!--Two-way data-binding to the name property-->
<span data-link="name"></span> <!--Will update when name property changes-->
HTML elements within 'top-level' page content can also be data-linked -- see below.
With JsRender, you call the render()
method, then insert the resulting HTML in the DOM.
var html = tmpl.render(data, helpersOrContext);
$("#container").html(html);
With JsViews, you can instead call the link()
method:
tmpl.link("#container", data, helpersOrContext);
which in one line of code will:
- render the template
- insert the resulting HTML as content under the HTML
container
element - data-link that content to the underlying
data
Now observable changes in the data will automatically trigger updates in the rendered UI.
There are two ways of calling the link()
method:
- If you have a reference to the template object, call
template.link(...)
- If you have registered the template by name (
"myTmpl"
), calllink.myTmpl(...)
Example: - Template from string
var tmpl = $.templates("{^{:name}} <input data-link='name' />");
var person = {name: "Jim"};
tmpl.link("#container", person);
Example: - Template from script block
<script id="myTemplate" type="text/x-jsrender">
{^{:name}} <input data-link="name" />
</script>
var tmpl = $.templates("#myTemplate");
var person= {name: "Jim"};
tmpl.link("#container", person);
Example: - Named template from string
$.templates("myTmpl1", "{^{:name}} <input data-link='name' />");
var person= {name: "Jim"};
$.link.myTmpl1("#container", person);
Example: - Named template from script block
<script id="myTemplate" type="text/x-jsrender">
{^{:name}} <input data-link="name" />
</script>
$.templates("myTmpl2", "#myTemplate");
var data = {name: "Jim"};
$.link.myTmpl2("#container", data);
Result: After each link()
example above the container
element will have the following content:
Jim <input value="Jim" />
with the name
property of person
object data-linked to the "Jim"
text node and two-way data-linked to the <input />
See: Playing with JsViews for working samples, such as this one
You can use data-linking not only for templated content, but also to data-bind to top-level HTML content in your page:
$.link(true, "#target", data);
This will activate any declarative data-binding (data-link="..."
expressions) on the target element - or on elements within its content.
In current JavaScript implementations, modifying objects or arrays does not raise any event, so there is no way for the change to be detected elsewhere. JsViews dynamic data-bound UI solves this through data-linking, using the JsObservable observer pattern.
The JsViews $.observable()
API provides a way for you to change objects or arrays observably. Each change will raise a property change or array change event.
Modify an object observably
$.observable(person).setProperty("name", newName);
$.observable(person)
makes the person
object "observable", by providing a setProperty(...)
method. Use setProperty
to change a value, and the change will be "observed" by the declarative data-binding in the template.
Modify an array observably
$.observable(people).insert(newPerson);
$.observable(people)
makes the people
array "observable", by providing methods like insert(...)
and remove(...)
. Use them to make changes to arrays, and the changes will be "observed" by data-bound elements and tags in the template - such as the {^{for dataArray}}
tag.
JsViews uses the property change or array change events to make any data-linked tags or elements in your templates update automatically in response to each observable change in your underlying data. In addition, with two-way data-linking, it ensures that those events are raised when the user interacts with a data-linked template, and causes changes to the underlying data.
observe() and observeAll()
The $.observe()
and $.observable().observeAll()
APIs make it very easy for you to register event handlers or listeners, so your code can listen to specific observable changes made to your data objects or view models:
$.observe(person, "name", function(...) {
// The "name" property of person has changed
...
});
$.observable(person).observeAll(function(...) {
// A property of person, or a nested object property, has changed
...
});
Each instance of a rendered template or a template block tag is associated with a JsViews "view" object -- so nested tags lead to a hierarchy of view objects. The view hierarchy shows how the underlying data objects map to the rendered UI.
From UI back to data:
Use $.view(elem)
to get from a DOM element to the corresponding view
object for that part of the rendered content. From the view
you can then get to the underlying data
, the index
, etc.
{^{for people}}
...
<button class="changeBtn">Change</button>
...
{{/for}}
Click-handler code for Change button:
$(".changeBtn").on("click", function() {
// From the clicked HTML element ('this'), get the view object
var view = $.view(this);
// Get the 'person' data object for clicked button
var person = view.data;
// Get index of this 'item view'. (Equals index of person in people array)
var index = view.index;
// Change the person.name
$.observable(person).setProperty("name", person.name " " index);
});
JsViews data-linked templates (and the $.observe()
API) use the same paths and expressions as JsRender templates, but in addition provide 'leaf' data-binding -- such as:
{^{:team.manager.name`}} <!--updates when name changes-->
<span data-link="team.manager.name"></span> <!--updates when name changes-->
<input data-link="team.manager.name" /> <!--two-way binding to name-->
But data-linked paths have additional support, such as linking deeper into paths:
{^{:team^manager.name}} <!--updates when name, manager, or team changes-->
JsViews also allows you to data-bind to computed values, such as:
{^{:shoppingCart.totalAmount()}} <!--updates when totalAmount() changes-->
<input data-link="person.fullName()" /> <!--two-way binding, computed fullName()-->
See the www.jsviews.com site, including the JsViews Quickstart, JsViews APIs and JsObservable APIs topics.
Demos and samples can be found at www.jsviews.com/#samples, and throughout the API documentation.
(See also the demos folder of the GitHub repository - available here as live samples).