DocumentationThe Boxely UI Toolkit has built-in facilities for creating a relationship between a Boxely scene element and a data source. A box or gadget that is "data bound" is a user interface element that has one or more properties directly associated with one or more properties of a data source. Once the relationship has been established, any changes made to the data source can be reflected in the bound element and changes made to the bound element can be replicated back to the data source.
Currently, you can bind data between elements, between elements and script objects, and between elements and business objects (either script objects or OCP service objects).
To introduce data binding, let’s take a relatively simple scenario. Suppose you want to create a relationship between an input box (the source element) and another input box (a bound element) where changes made to the source are automatically reflected in the bound element and vice versa. This type of relationship is referred to as "two-way data binding" and is depicted in the following images:

As the user changes the source element, the bound element is automatically updated.

As the user changes the bound element, the source element is automatically updated.
To program this type of functionality, you must first create the source element and then create the bound element with two-way binding, as shown in the following code:
<?xml version="1.0" encoding="utf-8" ?>
<?import href="box://ocpToolkit/theme/default/toolkit.box"?>
<?import href="box://ocpToolkit/content/core/coreGadgets.box"?>
<window xmlns="http://www.aol.com/boxely/box.xsd"
xmlns:s="http://www.aol.com/boxely/style.xsd"
xmlns:on="http://www.aol.com/boxely/reaction.xsd"
s:minHeight="125"
s:minWidth="475"
s:maxHeight="125"
s:maxWidth="475"
title="Two-Way Data Binding"
s:fill="white">
<vbox>
<hbox flex="1" s:height="10" />
<label value="Two-way Data Binding" s:fontBold="true" />
<hbox flex="1" s:height="10" />
<hbox>
<label value="Source Element:" />
<!-- Source element -->
<input id="sourceData" value="Some Value" s:width="360" />
</hbox>
<hbox flex="1" s:height="10" />
<hbox>
<label value="Bound Element:" s:fontFamily="Arial" />
<!-- Bound element with two-way data binding -->
<input value="" s:width="360" >
<binding elementSource="sourceData" targetProperty="value" path="value"
method="twoWay" />
</input>
</hbox>
</vbox>
</window>
In the above code, the source element is an input gadget with the "sourceData" id.
The bound element is also an input gadget; it contains the child binding element, which binds the parent to the source identified in the elementSource attribute. The targetProperty attribute indicates which property of the bound element should be updated when a change occurs, and the path attribute indicates the property of the source element to use in the update. The method attribute indicates whether the binding is one-way or two-way. With two-way binding, changes made to the source are reflected in bound boxes, and changes made to a bound box are reflected in the source.
The Boxely UI Toolkit also supports one-way binding, where changes made to the source are reflected in bound boxes, but changes made to a bound box are not reflected in the source, as shown in the following images:

As the user changes the source element, the bound element is automatically updated.

As the user changes the bound element, the source element remains is unaffected.
The following code demonstrates how to do this:
<?xml version="1.0" encoding="utf-8" ?>
<?import href="box://ocpToolkit/content/core/coreGadgets.box"?>
<?import href="box://ocpToolkit/theme/default/toolkit.box"?>
<window xmlns="http://www.aol.com/boxely/box.xsd"
xmlns:s="http://www.aol.com/boxely/style.xsd"
xmlns:on="http://www.aol.com/boxely/reaction.xsd"
s:minHeight="125"
s:minWidth="475"
s:maxHeight="125"
s:maxWidth="475"
title="One-Way Data Binding"
s:fill="white">
<vbox>
<hbox flex="1" s:height="10" />
<label value="One-way Data Binding" s:fontBold="true" />
<hbox flex="1" s:height="10" />
<hbox>
<label value="Source Element:" />
<!-- Source element -->
<input id="sourceData" value="Some Value" s:width="350" />
</hbox>
<hbox flex="1" s:height="10" />
<hbox>
<label value="Bound Element:" s:fontFamily="Arial" />
<!-- Bound element with one-way data binding -->
<input value="" s:width="350" >
<binding elementSource="sourceData" targetProperty="value" path="value"
method="oneWay" />
</input>
</hbox>
</vbox>
</window>
In the above code, notice that the binding element’s method attribute is set to "oneWay" indicating a directional relationship.
The Boxely UI Toolkit also supports collection binding, where you can bind a template (defined in a .box file) to an array of elements (defined in a JavaScript file). For example, you might use collection binding to populate a list of items in a scene, as shown in the following image.

When the user clicks "Bind To Collection", the list of email items populates.
To implement this scenario, you must first create the element that will store the list in your .box file. Then, within that element, you must specify the binding rules that will bind the source data to each item in the list.
<?xml version="1.0" encoding="UTF-8"?>
<?import href="../../../../ocpSamples/theme/samples.box"?>
<window xmlns="http://www.aol.com/boxely/box.xsd"
xmlns:s="http://www.aol.com/boxely/style.xsd"
xmlns:on="http://www.aol.com/boxely/reaction.xsd" chromeless="true"
id="window" translucent="false" s:height="470" s:width="650"
s:mainWidth="500" title="Databound List Population" resizable="true">
<code id="main" language="jscript" src="DataBoundListSample.js"/>
<vbox style="bordered">
<hbox s:vAlign="center" s:hAlign="center">
<button label="Bind To Collection" on:click="onBind();"/>
</hbox>
</vbox>
<listBox id="listBox" s:flex="1" selectMode="multiple">
<columns header="true">
<column id="column1" name="From" s:minWidth="250"/>
<column id="column2" name="To" s:minWidth="250"/>
<column id="column3" name="Subject" s:flex="1"/>
</columns>
<rows id="myRows">
<binding collectionSource="EMailItems" method="oneWay" replication="full">
<template>
<listItem style="rowContainer">
<label s:fontFamily="Arial" value="{from}" />
<label s:fontFamily="Arial" value="{to}" />
<label s:fontFamily="Arial" value="{subject}" s:flex="1"/>
</listItem>
</template>
</binding>
</rows>
</listBox>
</window>
In the above code, the bound element is a list of email items. Each item includes a "From", "To", and "Subject" field. At runtime, these items will display in an listBox containing three columns and an undefined number of rows. Within the rows element, the binding element connects the rows to the source object (in our case, a collection named "EmailItems"). The template element contained within the binding element associates data in the source object with the appropriate row container.
Once you’ve defined the bound element in the .box file that describes your scene, you can create the JavaScript code that describes the source element:
function myEmailItem()
{
this.from = "";
this.to = "";
this.subject = "";
}
function onBind()
{
var collection = null;
collection = shell.serviceManager.basics.array;
var emailItem1 = new myEmailItem();
emailItem1.from = "lovemonster@cupid.com";
emailItem1.to = "marco@polo.com";
emailItem1.subject = "Let’s meet!";
collection.addValue(emailItem1);
var emailItem2 = new myEmailItem();
emailItem2.from = "imaginaryfriend@bouncebackemails.com";
emailItem2.to = "marco@polo.com";
emailItem2.subject = "Long time no speak...";
collection.addValue(emailItem2);
var emailItem3 = new myEmailItem();
emailItem3.from = "one@dummydomain.com";
emailItem3.to = "marco@polo.com";
emailItem3.subject = "1 2 3";
collection.addValue(emailItem3);
scene.EMailItems = collection;
}
In the above code, the myEmailItem function defines the JavaScript object that represents an item in the collection. In the onBind function, we instantiate the myEmailItem object three times, adding it to our collection object (a basics.array). As our last step, we set a DOM object in the scene equal to the our collection object (the basics.array), which binds the data to the specified scene element.
Now that you’ve been introduced to data binding and collection binding, let’s try it out in the "To-Do List" application. Proceed to the next section, "To-Do List" Exercise 9.1: Using data binding.
In this exercise, you will create a data source object that represents a "to-do" note and bind the elements of the noteArea gadget to this object. All changes made to the data source object will be automatically reflected in bound boxes.
To complete the exercise, follow these steps:
Step 1: Create a new data source object named NoteItem - In a new JavaScript file, define an object that represents a "to-do" note, as well as the functions needed to manage data within the object.
Step 2: Instantiate the NoteItem object and add it to the DOM of the scene - In the noteArea gadget’s onInitialized function, instantiate the NoteItem object and then use the addDOMObject method to add the NoteItem object to the DOM of the scene.
Step 3: Change the code that modifies noteArea parts to use the data source object - Change the JavaScript functions that manipulate "to-do" item parts directly to use the NoteItem data object instead.
Step 4: Bind the parts of the noteArea gadget to the data source object - In the noteAreaGadgets.box file, bind the parts of the noteArea gadget to the data source object.
Step 5: Testing your application - Run your application and verify that you can create and view "to-do" items.
Step 1: Create a new data source object named NoteItem
To get started, you must first define a data source object that represents a "to-do" note, as well as the functions needed to manage data within the object.
To do so, create a new JavaScript file named NoteItem.js and add code to:
Create the NoteItem JavaScript object and declare the following child properties: title, date, image, and description.
Create the following functions to manage the value stored in each property: setTitle, setDate, setImage, and setDescription. These functions must also call the notifyPropertyChange function, which loops through all listeners for the object and notifies the listener of the change made to the property.
Create the following required internal functions:
implementsProtocol - This function must be defined in the data-bound JavaScript object so that the Boxely UI Toolkit can determine if a box may be bound to the data object. Note: data-bound JavaScript objects must implement the EEObjectSource protocol, which requires the following functions: addObjectListener, removeObjectListener, and notifyPropertyChange. The Boxely UI Toolkit calls the implementsProtocol function passing the protocol named ‘EEObjectSource’ as a parameter to assure that your JavaScript object implements the EEObjectSource protocol.
addObjectListener - The Boxely UI Toolkit calls this function when we bind a box to the data object; it adds the bound box to an array of listeners.
removeObjectListener - The Boxely UI Toolkit calls this function when we unbind a box from the data object; it removes the bound box from the array of listeners.
notifyPropertyChange - This function loops through all listeners for the object and notifies the listener of the change made to the JavaScript object.
You can create these functions on your own. Or, to save time, you can use the NoteItem.js exercise file, which is located in the ToDoList/common directory.
The contents of the NoteItem.js file are shown here:
function NoteItem() {}
NoteItem.prototype.title = "";
NoteItem.prototype.date = "";
NoteItem.prototype.image = "";
NoteItem.prototype.description = "";
NoteItem.prototype.listeners = [];
NoteItem.prototype.setTitle = function (title)
{
this.title = title;
this.notifyPropertyChange("title");
}
NoteItem.prototype.setDate = function (date)
{
this.date = date;
this.notifyPropertyChange("date");
}
NoteItem.prototype.setImage = function (image)
{
this.image = image;
this.notifyPropertyChange("image");
}
NoteItem.prototype.setDescription = function (description)
{
this.description = description;
this.notifyPropertyChange("description");
}
NoteItem.prototype.addObjectListener = function (object)
{
this.listeners.push(object);
}
NoteItem.prototype.implementsProtocol = function (protocol)
{
return (protocol == "EEObjectSource");
}
NoteItem.prototype.removeObjectListener = function (object)
{
for (i = 0; i < this.listeners.length; i++)
{
if (this.listeners[i] == object)
{
this.listeners[i] = null;
this.listeners.splice(i, 1);
break;
}
}
}
NoteItem.prototype.notifyPropertyChange = function (property)
{
if (this.listeners != null)
{
for (i = 0; i < this.listeners.length; i++)
{
if (this.listeners[i] != null)
{
this.listeners[i].onPropertyChanged(this, property);
}
}
}
}
Step 2: Instantiate the NoteItem object and add it to the DOM of the scene
Next, in the noteArea gadget’s onInitialized function, you must instantiate the NoteItem object and then use the addDOMObject method to add the NoteItem object to the DOM of the scene. (By adding the NoteItem object to the DOM of the scene, it is made available to all JavaScript and .box files that make up the application.)
Open the noteAreaGadgets.js file and add the code shown below in bold to complete this step:
component.prototype.onInitialized = function()
{
this.todoItem = new NoteItem();
this.scene.addDOMObject("todoItem", this.todoItem);
}
Step 3: Change the code that modifies noteArea parts to use the data source object
Now that you’ve created the data source object and instantiated it in the application, you need to change the JavaScript functions that manipulate the "to-do" item data to use the data object’s methods.
In the noteAreaGadget’s setDisplay function, locate the four statements that use the setAttribute method to update parts within the gadget:
this._gadget.setAttribute("title", item["title"]);
this._gadget.setAttribute("date", formatDate(item["date"]));
this._gadget.setAttribute("description", item["description"]);
this._gadget.setAttribute("image", item["image"]);
Replace these statements with the relevant todoItem methods shown here:
// Using Data Binding this.todoItem.setTitle(item["title"]); this.todoItem.setDate(item["date"]); this.todoItem.setDescription(item["description"]); this.todoItem.setImage(item["image"]);
In the noteAreaGadget’s blankDisplay function, locate the four statements that use the setAttribute method to update parts within the gadget:
this._gadget.setAttribute("title", "[no note to display]");
this._gadget.setAttribute("date", "");
this._gadget.setAttribute("description",
"Instructions: Add a note with the button 'Create To-Do Item' below.");
this._gadget.setAttribute("image", gDefaultImage);
Replace these statements with the relevant todoItem methods shown here:
this.todoItem.setTitle("[no note to display]");
this.todoItem.setDate("");
this.todoItem.setDescription
("Instructions: Add a note with the button 'Create To-Do Item' below.");
this.todoItem.setImage(gDefaultImage);
Step 4: Bind the parts of the noteArea gadget to the data source object
To bind the parts of the noteArea gadget to the NoteItem data source object, use this procedure:
Open the noteAreaGadgets.box file and add the script element shown below in bold to load the NoteItem.js file into the gadget.
<gadget id="noteArea" language="jscript"
code="noteAreaGadgets.js" >
<attributes title="" date="" description="" image="" />
<script id="common" language="jscript"
href="../../common/common.js" />
<script id="NoteItem" language="jscript"
href="../../common/NoteItem.js"/>
<parts>
<box:vbox id="itemContainer" dragMode="data" >
<box:label ignoreEvents="true" id="itemTitle"
inherits="value=title" />
<box:label ignoreEvents="true" id="itemDate"
inherits="value=date" />
<box:vbox ignoreEvents="true" id="descriptionContainer" >
<box:multilineLabel ignoreEvents="true" id="itemDescription"
inherits="value=description" />
</box:vbox>
<box:image ignoreEvents="true" id="itemImage" inherits="src=image" />
</box:vbox>
</parts>
<behavior>
<reaction event="initialized" action="gadget:onInitialized();" />
<reaction event="dragStart" action="gadget:onDragStart();" />
</behavior>
</gadget>
Bind the relevant parts of the noteArea gadget to the data source object, using the code shown below in bold. (You can delete the inherits attribute as it is no longer needed.)
<parts>
<box:vbox id="itemContainer" dragMode="data" >
<box:label ignoreEvents="true" id="itemTitle" >
<box:binding objectSource="todoItem" targetProperty="value"
path="title" method="oneWay" />
</box:label>
<box:label ignoreEvents="true" id="itemDate" >
<box:binding objectSource="todoItem" targetProperty="value"
path="date" method="oneWay" />
</box:label>
<box:vbox ignoreEvents="true" id="descriptionContainer" >
<box:multilineLabel ignoreEvents="true" id="itemDescription" >
<box:binding objectSource="todoItem" targetProperty="value"
path="description" method="oneWay" />
</box:multilineLabel>
</box:vbox>
<box:image ignoreEvents="true" id="itemImage" inherits="src=image" >
<box:binding objectSource="todoItem" targetProperty="src"
path="image" method="oneWay" />
</box:image>
</box:vbox>
</parts>
Step 5: Testing your application
Now when you run your application, the "to-do" list items that you create and view are bound to the NoteItem data source object. When the data source object is modified, the changes will automatically be reflected in the noteArea gadget.