TIBET Logo

TIBET Data Binding

Data Binding

wins

  • Bind authoring happens almost entirely in markup via bind: attributes.
  • Bind data references use industry-standard URI and query path syntax.
  • You can quickly bind to JSON, XML, or JavaScript objects with equal ease.
  • Clear syntax differentiates binds from templating ([[...]] vs {{...}}).
  • Signal-driven operation makes for easy testing and mocking.

contents

concepts

cookbook

code


concepts

In the context of web applications, data binding is the common term used to describe observation of changes to one or more Model objects by one or more View objects in a user interface. As such, data binding is a central feature MV*C architectures and hence most modern web frameworks.

In TIBET, data binding is syntactic sugar for setting up observations of Change signals coming from a source object. For a two-way bind there's a second layer of sugaring which maps a Change in a View to a set() on a target object. All binds are ultimately implemented via Signals.

TIBET implements one-way and bi-directional data binding via attributes in the TIBET-specific bind: namespace. You can also create binds using double-brackets [[ ]] within markup, particularly within attribute values. For example, this markup uses both a bind:scope and [[ ]] syntax:

<input type="text" bind:scope="urn:tibet:currentEmployee" value="[[lname]]"/>

In the example below the xctrls:list is scoped to ~/dat/codes:

<body bind:scope="~/dat">
    <div bind:scope="codes">
        <xctrls:list bind:in="{data: states.json}"/>
    </div>
</body>

All bind: sources and sinks are represented by TIBET URIs, a standard way to refer to resources in the web and in TIBET. All specific references use standards-based TIBET Paths such as JSONPath or XPath depending on the source data format.

Using TIBET's bind:scope, bind:in, bind:out, and bind:io attributes along with more advanced options such as bind:repeat, you can quickly and easily bind data to your UI regardless of whether it is JSON, XML, or a random object.

bind:scope

The bind:scope attribute lets you define a scope on an outer element which will serve as a root for any relative bind paths below that scope. This is particular useful on elements like forms or tables where a number of individual properties or rows may benefit from shared scope:

<form bind:scope="urn:tibet:CurrentEmployee">
...
    <input bind:io="firstname"/>
...
</form>

Values in bind:scope attributes found along a UI element's ancestor chain are combined as needed, supporting nested scoping.

bind:io

Most TIBET binds are bidirectional and use bind:io as their directional attribute to signify the target control both receives input and sends output to the bind:

<input type="text" bind:io="value"/>

bind:in

When dealing with read-only UI controls you can use bind:in to signify that the control receives input from the bind but does not send output to it:

<span bind:in="urn:tibet:hello"/>

bind:out

While not common, it's also possible to bind output separately from input. This can be used to create more interesting data flows via binds:

<unique:widget bind:out="urn:tibet:hello"/>
Bind Values

Simple binds often reference an absolute or relative URI with no specific query. When using the simple form the bind will default to a query of 'value' and invoke get('value') on the resolved URI to acquire the value of the bind.

You can include a query with the URI (or as a standalone bind component) using XPointer syntax (a fragment # specifier followed by a recognized query). Common query syntax options include: xpath1 (XPath 1.0), jpath (JSON Path), and tibet (our own JavaScript path syntax):

<!-- XPath -->
<span bind:scope="urn:tibet:people#xpath1(/people/person[1])">
    First name (bind:io): <textarea bind:io="firstname"></textarea>
</span>


<!-- TIBET path (with slicing) -->
<input type="text" bind:io="{value: urn:tibet:people#tibet(people[0].firstname)}"/>

More complex binds can provide a simplified JavaScript object string to define how the bind should operate:

<input type="text" bind:io="{value: 'The index: [[$INDEX]]'}"/>

As shown in the example above, TIBET binds can include references to a small number of internal variables including the current iteration $INDEX.

Binds also support the TIBET Shell (TSH) formatter syntax via .%. In the example below we're formatting the current item's 'middlename` slot to be upper case:

<input type="text" bind:io="{value: 'The middle name: [[$_.middlename .% upperCase]]'}"/>

To target specific aspects of your source or sink TIBET allows you to use an appropriate query syntax for the data in question such as JSONPath, CSS Queries, XPath, or expanded TIBET path syntax (see TIBET Paths for details).

For attribute values such as class= you can use a double-bracket syntax [[ ]] to create binds. You can also use this syntax within element content. We use [[ ]] for binds to avoid conflicts with {{ }} syntax which is used for TIBET Templating.

<span bind:scope="urn:tibet:currentEmployee">[[lname]]</span>

The core machinery of data binding, namely Change signals triggered from set() calls, is univeral in TIBET. All TIBET-derived objects automatically implement set() and can trigger Change signals if desired. In TIBET there is no unique Model type or associated inheritance requirement. Any object can serve as a Model, provided you name it.

All binds in TIBET ultimately refer to a TIBET URI object. TIBET URIs manage their data content including being able to snapshot data for undo processing. A specific URI subtype, TP.core.URN, provides a way to give any data a public name and a URI reference, allowing you to bind to any object.

Data binding in TIBET is fully tooled. When you're using the Sherpa your current bind information is automatically displayed in the Sherpa's HUD display for easy reference and manipulation.


cookbook

Coming soon...


code

Binds make use of a broad array of functionality from across TIBET, from signaling to URIs to set() methods to tag attach/detach processing in the DOM.

Dozens of examples of binds can be found in ~/lib/test/src/bind.

Each test will typically ensure a data source, a markup template, and test criteria. The markup template will show you the particular bind syntax being used while the data source will help you analyze how the bind paths are accessing the data.

The "driver" file TP.bind_Tests.js manages loading the individual .xhtml files containing the bind syntax examples you're probably looking for. Once each page loads the driver checks that their values match expectations.

Binding, at least UI binding, in TIBET is managed by code in the bind: namespace.found in ~lib/src/bind and the TIBET kernel file TIBETBinding.js.

Since all binds in TIBET reference URIs of various forms the TIBETURITypes.js file in the TIBET kernel handles most URI-related functionality.

Finally, since all binds are essentially just sugared change notifications the TIBETSignaling.js file and set() methods which trigger change notifications are central to how binding operates.