Interactive data-driven views, MVVM and MVP, built on top of JsRender templates
Clone or download
BorisMoore Commit 91 (v0.9.91 - Beta)
Major update

Breaking change

- Templates declared in <div> or other non script element are not
  supported. (Throws with error message)

Minor breaking changes

- The {{range}} tag has been removed - replaced by {{for}} tag using
  built-in range features. See https://www.jsviews.com#jsvfortag@jsvsortfilterrange

- Changed behavior of .setValue() API on custom tags. The .setValue()
  method is called even for undefined values. The .getValue() method
  is not called. Return false from setValue to not data-link a linked
  element. (e.g. jQuery UI slider). See https://www.jsviews.com#tagoptions@setvalue

- {{tabs}} API changes: The selectedIndex property is renamed to the
  pane property. The tabCaption property is renamed to the caption
  property

- Some changes to behavior/effect of the bindTo tag option - which is
  now associated with the new bindFrom tag option.
  See https://www.jsviews.com#tagoptions@bindto and https://www.jsviews.com#tagoptions@bindfrom

Major feature improvements

- The {{for}} tag now has built-in support for sort, filter, start and
  end. See https://www.jsviews.com#fortag@sortfilterrange

- The {{props}} tag now has built-in support for sort, filter, start
  and end. See https://www.jsviews.com#propstag@sortfilterrange

- New converters encode/unencode. See https://www.jsviews.com#convertersapi@encode

- New tag options: bindFrom - together with tag option bindTo - provide
  improved features for custom tags.
  See https://www.jsviews.com#tagoptions@bindto and https://www.jsviews.com#tagoptions@bindfrom

- New support for observing/unobserving contextual parameters.
  $.observe(tagOrView, "~foo", callback) - documentation to follow

- New support for observing properties of any item in an array, using
  "array.[].*" or "array.[].foo" (or "array.[]^*", etc.) - documentation to follow

- Late paths support: "@a.b.c" - documentation to follow

- New support for getting a reference to a tag instance using
  {^{mytag this=~myTag}} - documentation to follow

Minor feature improvements

- Custom tag linkedElem can now target non-editable and contentEditable.
  See https://www.jsviews.com#bindingpatterns@linkedelem

- New APIs added for tagCtx.ctxPrm(), tagCtx.cvtArgs() and
  tagCtx.bndArgs() even for non-data-linked tags - documentation to follow

- The contentCtx option now works also for custom tag using render(),
  rather than a template. See https://www.jsviews.com#tagsapi@contentctx

- In a template, ~tag.tagCtx.xxx can now be written ~tagCtx.xxx,
  and works correctly e.g. for data-linking using
  {{mytag}}{{else}}{{~tagCtx...}}{{/mytag}} - documentation to follow

- ~tagCtx is now a reserved name like ~tag ~root ~parentTags...

- On a custom tag, the bindTo option is not needed with 2-way linking
  to first argument

- updateValue() now supports an async option: pass true for the fourth
  parameter, defer - updateValue(val, index, tagElse, tag, defer).
  - Documentation to follow

- New debug:true option on a compiled template.
  See https://www.jsviews.com#d.templates@debug

- New depends option on custom tags (see https://www.jsviews.com#tagoptions@depends)
  or an instance property {{someTag depends='a.b'}} or
  {{someTag depends=~someTagDependsArray}}

- An error will be thrown if different versions of jsrender.js,
  jquery.observable.js and  jquery.views.js are loaded together

- DataMap, {{props}} and {{jsonview}} now include support for function
  properties too, unless opt out using {{props foo noFunctions=true}}.
  See https://www.jsviews.com#propstag@nofunctions

- Support for nested {{for}} tags without iteration, and for then
  applying operations such as sorting to arrays in nexted context, as in
  {{for filter=... noIteration=true}}{{for sort=... noIteration=true}}{{for start=...  end=...}}.
  See https://www.jsviews.com#fortag@itemvar

Documentation

- Extensive new documentation, especially on custom tag controls:
  See https://www.jsviews.com#jsvtagcontrols. For an example of a JsViews
  custom tag control see https://www.jsviews.com#samples/tag-controls/colorpicker

Minor bug fixes, including:

- a contentEditable bug for IE

- a bug for data-linking to 'length' property.

- a bug when a computed property 'depends' mixes data and contextual paths

- a bug in jquery-1.x cleanData

- a bug in move() arrayChange on {{for}}

- Issue #408
  $.observe('ns', model.list, '[]^qty', handler);??

- Issue #406
  Weird data-linked `for` loop behaviour with deep observing after model update

- Issue #404
  Props Convert else statement not working

- Issue #403
  ~ operator support

- Issue #400
  Move the tag property in views to before rendering, to enable "get current path" path scenario

- Issue #398
  After DOM modifications to the child options of a data-linked select, the first option shows as selected

- Issue #397
  {^{radiogroup value disabled=notEnabled}} is now supported

- Issue #198
  Provide full documentation of custom tags using 2-way binding, binding to args or props etc.

- Issue #374
  Wrong initial value shown (timespinner control)

- Issue BorisMoore/jsrender#335
  encode and unencode converters
Latest commit 3f2ae07 Sep 21, 2018

README.md

JsViews: next-generation MVVM and MVP framework - bringing templates to life

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 and downloads

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 installation

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:

(Note that jsviews.js includes all of jsrender.js code -- so jsrender.js does not need to be loaded first.)

JsRender and JsViews

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 usage

Data-linked templates

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>

Learn more...

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.

Learn more...

Render and link a template

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:

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

Learn more...

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.

Learn more...

Making "observable" changes to objects and arrays

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.

Learn more...

Responding to data changes

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
  ...
});

Learn more...

Accessing the view hierarchy

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.

Example:

{^{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);
});

Learn more...

Data-linked paths

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-->

Learn more...

Computed observables

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()-->

Learn more...

Documentation and APIs

See the www.jsviews.com site, including the JsViews Quickstart, JsViews APIs and JsObservable APIs topics.

Demos

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).