blob: 5aa6c57d4a69fff338f677586a383a3321bb7322 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<document id="data-binding">
<properties>
<title>Data Binding</title>
</properties>
<body>
<p>
Data binding refers to the process of automatically populating or extracting data from
a set of user interface elements. In Pivot, data binding is driven primarily by two
methods of the <tt>Component</tt> class: <tt>load()</tt> and <tt>store()</tt>. Each
method takes an <tt>Object</tt> argument called the "context". The context is either an
instance of a Java bean class or an instance of
<tt>org.apache.pivot.collections.Dictionary</tt>. Calling <tt>load()</tt> causes data
from the context to be "loaded" into the component; calling <tt>store()</tt> performs
the reverse operation and "stores" data from the component into the context. A third
method, <tt>clear()</tt> allows a caller to reset any bindings.
</p>
<p>
Components that support data binding provide "key" properties that allow a caller to
associate a property value with a value in the bind context. For example, the
<tt>Label</tt> class provides a "textKey" property that maps the label's "text" property
to a value provided by the context.
</p>
<p>
The following application demonstrates data binding. It allows the user to load a form
with address data either from a JSON file or from a Java bean object, as well as clear
the form. Note that binding to a Java bean is accomplished by wrapping the bean in an
instance of <tt>org.apache.pivot.beans.BeanAdapter</tt> before passing it to the
<tt>load()</tt> method:
</p>
<application class="org.apache.pivot.wtk.ScriptApplication" width="430" height="280">
<libraries>
<library>core</library>
<library>wtk</library>
<library>wtk-terra</library>
<library>tutorials</library>
</libraries>
<startup-properties>
<src>/org/apache/pivot/tutorials/databinding/data_binding.bxml</src>
</startup-properties>
</application>
<p>
The BXML simply sets up the form structure and the bind keys:
</p>
<source type="xml" location="org/apache/pivot/tutorials/databinding/data_binding.bxml">
<![CDATA[
<databinding:DataBinding title="Data Binding" maximized="true"
xmlns:bxml="http://pivot.apache.org/bxml"
xmlns:databinding="org.apache.pivot.tutorials.databinding"
xmlns="org.apache.pivot.wtk">
<Border styles="{padding:6}">
<BoxPane orientation="vertical" styles="{spacing:10, fill:true}">
<Form bxml:id="form" styles="{showFlagIcons:false}">
<Form.Section>
<Label bxml:id="sourceLabel" Form.label="Source" styles="{font:{italic:true}}"/>
<Label Form.label="ID" textKey="id"/>
<TextInput Form.label="Name" textKey="name"/>
<BoxPane Form.label="Address" orientation="vertical">
<TextInput textKey="address.street" prompt="Street"/>
<BoxPane>
<TextInput textKey="address.city" prompt="City"/>
<TextInput textKey="address.state" textSize="6" prompt="State"/>
<TextInput textKey="address.zip" textSize="6" prompt="Zip"/>
</BoxPane>
</BoxPane>
<TextInput Form.label="Phone" textKey="phoneNumber"/>
<TextInput Form.label="Email" textKey="emailAddress"/>
<BoxPane Form.label="IM">
<TextInput textKey="imAccount.id"/>
<ListButton selectedItemKey="imAccount.type"
listData="['AIM', 'Jabber', 'Yahoo']"/>
</BoxPane>
</Form.Section>
</Form>
<Separator/>
<BoxPane styles="{horizontalAlignment:'right'}">
<PushButton bxml:id="loadJSONButton" buttonData="Load JSON"/>
<PushButton bxml:id="loadJavaButton" buttonData="Load Java"/>
<PushButton bxml:id="clearButton" buttonData="Clear"/>
</BoxPane>
</BoxPane>
</Border>
</databinding:DataBinding>
]]>
</source>
<p>
The windows's <tt>initialize()</tt> method defines the button press listeners that
load or clear the form:
</p>
<source type="java" location="org/apache/pivot/tutorials/databinding/DataBinding.java">
<![CDATA[
package org.apache.pivot.tutorials.databinding;
import java.io.InputStream;
import java.net.URL;
import org.apache.pivot.beans.BeanAdapter;
import org.apache.pivot.beans.Bindable;
import org.apache.pivot.collections.Map;
import org.apache.pivot.json.JSONSerializer;
import org.apache.pivot.util.Resources;
import org.apache.pivot.wtk.Button;
import org.apache.pivot.wtk.ButtonPressListener;
import org.apache.pivot.wtk.Form;
import org.apache.pivot.wtk.Label;
import org.apache.pivot.wtk.PushButton;
import org.apache.pivot.wtk.Window;
public class DataBinding extends Window implements Bindable {
private Form form = null;
private PushButton loadJavaButton = null;
private PushButton loadJSONButton = null;
private PushButton clearButton = null;
private Label sourceLabel = null;
private static final Contact CONTACT = new Contact("101", "Joe User",
new Address("123 Main St.", "Cambridge", "MA", "02142"),
"(617) 555-1234", "joe_user@foo.com",
new IMAccount("juser1234", "AIM"));
@Override
public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
form = (Form)namespace.get("form");
loadJavaButton = (PushButton)namespace.get("loadJavaButton");
loadJSONButton = (PushButton)namespace.get("loadJSONButton");
clearButton = (PushButton)namespace.get("clearButton");
sourceLabel = (Label)namespace.get("sourceLabel");
loadJavaButton.getButtonPressListeners().add(new ButtonPressListener() {
@Override
public void buttonPressed(Button button) {
form.load(new BeanAdapter(CONTACT));
sourceLabel.setText("Java");
}
});
loadJSONButton.getButtonPressListeners().add(new ButtonPressListener() {
@Override
public void buttonPressed(Button button) {
JSONSerializer serializer = new JSONSerializer();
InputStream inputStream = getClass().getResourceAsStream("contact.json");
try {
form.load(serializer.readObject(inputStream));
sourceLabel.setText("JSON");
} catch(Exception exception) {
System.err.println(exception);
}
button.setEnabled(true);
}
});
clearButton.getButtonPressListeners().add(new ButtonPressListener() {
@Override
public void buttonPressed(Button button) {
form.clear();
sourceLabel.setText("");
}
});
}
}
]]>
</source>
<p>
The JSON representation of the sample contact record is defined as follows (note that,
while JSON is used to represent the data in this example, any class that implements
the <tt>Dictionary</tt> interface, including <tt>HashMap</tt>, can be used):
</p>
<source type="jscript" location="org/apache/pivot/tutorials/databinding/contact.json">
<![CDATA[
{ id: 101,
name: "Joe User",
address: {
street: "123 Main St.",
city: "Cambridge",
state: "MA",
zip: "02142"
},
phoneNumber: "(617) 555-1234",
emailAddress: "joe_user@foo.com",
imAccount: {
id: "juser1234",
type: "AIM"
}
}
]]>
</source>
<p>
The Java bean version, which represents the same data, is composed of the following
classes:
</p>
<source type="java" location="org/apache/pivot/tutorials/databinding/Contact.java">
<![CDATA[
package org.apache.pivot.tutorials.databinding;
public class Contact {
private String id;
private String name;
private Address address;
private String phoneNumber;
private String emailAddress;
private IMAccount imAccount;
public Contact(String id, String name, Address address, String phoneNumber,
String emailAddress, IMAccount imAccount) {
this.id = id;
this.name = name;
this.address = address;
this.phoneNumber = phoneNumber;
this.emailAddress = emailAddress;
this.imAccount = imAccount;
}
public String getID() {
return id;
}
public String getId() {
return getID();
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getEmailAddress() {
return emailAddress;
}
public IMAccount getIMAccount() {
return imAccount;
}
public IMAccount getImAccount() {
return getIMAccount();
}
}
]]>
</source>
<source type="java" location="org/apache/pivot/tutorials/databinding/Address.java">
<![CDATA[
package org.apache.pivot.tutorials.databinding;
public class Address {
private String street;
private String city;
private String state;
private String zip;
public Address() {
this(null, null, null, null);
}
public Address(String street, String city, String state, String zip) {
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getState() {
return state;
}
public String getZip() {
return zip;
}
}
]]>
</source>
<source type="java" location="org/apache/pivot/tutorials/databinding/IMAccount.java">
<![CDATA[
public class IMAccount {
private String id;
private String type;
public IMAccount() {
this(null, null);
}
public IMAccount(String id, String type) {
this.id = id;
this.type = type;
}
public String getID() {
return id;
}
public String getId() {
return getID();
}
public String getType() {
return type;
}
}
]]>
</source>
<p>
This application's data binding requirements are fairly straightforward. It is
load-only, and all of the data is simply presented as-is. However, many real-world
applications may want to transform the data in some way before presenting it to the
user; for example, an application may want to apply currency formatting to a numeric
value, or convert an encoded date string to an instance of
<tt>org.apache.pivot.util.CalendarDate</tt>. Components that support data binding
provide a "bind mapping" interface to facilitate such transformations. Bind mappings
are outside the scope of this example but are demonstrated in the
<a href="query-servlet.html">QueryServlet</a> section as well as the
<a href="stock-tracker.html">Stock Tracker</a> tutorial.
</p>
<p>
Also, though it is not shown in this example, bindable components allow a caller to
control the bind direction via a "bind type" property. The bind type is specified by
an instance of the <tt>org.apache.pivot.wtk.BindType</tt> enum, which defines the
following values:
</p>
<ul>
<li><tt>LOAD</tt> - binding will only occur during a <tt>load()</tt> operation</li>
<li><tt>STORE</tt> - binding will only occur during a <tt>store()</tt> operation</li>
<li><tt>BOTH</tt> - binding occur during both <tt>load()</tt> and <tt>store()</tt> operations</li>
</ul>
</body>
</document>