| <?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="stock-tracker.data-binding"> |
| <properties> |
| <title>Data Binding</title> |
| </properties> |
| |
| <body> |
| <p> |
| Data binding is the process of automatically mapping values between a set of user |
| interface elements and an internal data representation; for example, from a order entry |
| form to a collection of database fields or vice versa. Data binding can help simplify |
| development by eliminating some or all of the tedious boilerplate code that often goes |
| along with this type of programming. |
| </p> |
| |
| <p> |
| In Pivot, data binding is controlled by the <tt>load()</tt> and <tt>store()</tt> |
| methods of the <tt>Component</tt> class: |
| </p> |
| |
| <p> |
| <tt>public void load(Object context) {...}</tt><br/> |
| <tt>public void store(Object context) {...}</tt><br/> |
| </p> |
| |
| <p> |
| The <tt>Object</tt> value passed to these methods provides the "bind context". It |
| may be either an implementation of the <tt>org.apache.pivot.collections.Dictionary</tt> |
| interface or a Java bean value that can be wrapped in an instance of |
| <tt>org.apache.pivot.beans.BeanAdapter</tt>, which implements <tt>Dictionary</tt>. |
| </p> |
| |
| <p> |
| The bind context is essentially a collection of name/value pairs representing the data |
| to which the components will be bound. Each bindable property of a component can be |
| assigned a "bind key" that associates the property with a value in the context. Data is |
| imported from the context into the property during a load, and exported from the |
| property to the context during a store. Bind contexts may be flat or hierarchical; |
| JSON path syntax can be used to retrieve nested values (e.g. "foo.bar.baz" or |
| "foo['bar'].baz"). |
| </p> |
| |
| <p> |
| Many Pivot components support data binding including text inputs, checkboxes and radio |
| buttons, list views, and table views, among others. Some components support binding to |
| multiple properties; for example, a caller can bind to both the list data as well as the |
| selection state of a <tt>ListView</tt> component. |
| </p> |
| |
| <h3>Data Binding in Stock Tracker</h3> |
| |
| <p> |
| The Stock Tracker demo uses data binding to populate the fields in the quote detail |
| view. The bind context is actually the row data retrieved from the web query for the |
| selected stock. This is why the application requests more data than it seems to need |
| from the GET query: the extra fields are used to fill in the data in the detail form. |
| </p> |
| |
| <p> |
| The bound components, in this case, are labels - Stock Tracker maps values from the |
| retrieved quote data to the text property of each. The name of the key to use for each |
| label is specified via the "textKey" property: |
| </p> |
| |
| <source type="xml"> |
| <![CDATA[ |
| <Form styles="{padding:0, fill:true, showFlagIcons:false, showFlagHighlight:false, |
| leftAlignLabels:true}"> |
| <Form.Section> |
| <bxml:define> |
| <stocktracker:ValueMapping bxml:id="valueMapping"/> |
| <stocktracker:ChangeMapping bxml:id="changeMapping"/> |
| <stocktracker:VolumeMapping bxml:id="volumeMapping"/> |
| </bxml:define> |
| |
| <Label bxml:id="valueLabel" Form.label="%value" |
| textKey="value" textBindMapping="$valueMapping" |
| styles="{horizontalAlignment:'right'}"/> |
| <Label bxml:id="changeLabel" Form.label="%change" |
| textKey="change" textBindMapping="$changeMapping" |
| styles="{horizontalAlignment:'right'}"/> |
| <Label bxml:id="openingValueLabel" Form.label="%openingValue" |
| textKey="openingValue" textBindMapping="$valueMapping" |
| styles="{horizontalAlignment:'right'}"/> |
| <Label bxml:id="highValueLabel" Form.label="%highValue" |
| textKey="highValue" textBindMapping="$valueMapping" |
| styles="{horizontalAlignment:'right'}"/> |
| <Label bxml:id="lowValueLabel" Form.label="%lowValue" |
| textKey="lowValue" textBindMapping="$valueMapping" |
| styles="{horizontalAlignment:'right'}"/> |
| <Label bxml:id="volumeLabel" Form.label="%volume" |
| textKey="volume" textBindMapping="$volumeMapping" |
| styles="{horizontalAlignment:'right'}"/> |
| </Form.Section> |
| </Form> |
| ]]> |
| </source> |
| |
| <p> |
| The actual binding occurs when the selection changes in the table view; the selection |
| change handler calls the <tt>refreshDetail()</tt> method in response to a selection |
| change event: |
| </p> |
| |
| <source type="java"> |
| <![CDATA[ |
| @SuppressWarnings("unchecked") |
| private void refreshDetail() { |
| StockQuote stockQuote = null; |
| |
| int firstSelectedIndex = stocksTableView.getFirstSelectedIndex(); |
| if (firstSelectedIndex != -1) { |
| int lastSelectedIndex = stocksTableView.getLastSelectedIndex(); |
| |
| if (firstSelectedIndex == lastSelectedIndex) { |
| List<StockQuote> tableData = (List<StockQuote>)stocksTableView.getTableData(); |
| stockQuote = tableData.get(firstSelectedIndex); |
| } else { |
| stockQuote = new StockQuote(); |
| } |
| } else { |
| stockQuote = new StockQuote(); |
| } |
| |
| detailPane.load(new BeanAdapter(stockQuote)); |
| } |
| ]]> |
| </source> |
| |
| <p> |
| Note that the <tt>load()</tt> method is actually called on the detail pane itself |
| rather than on the parent container of the detail labels (an instance of <tt>Form</tt>). |
| This is because the application also needs to bind to the label that contains the |
| company name, which is not a child of the <tt>Form</tt>: |
| </p> |
| |
| <source type="xml"> |
| <![CDATA[ |
| <Label textKey="companyName" styles="{font:{size:12, bold:true}}"/> |
| ]]> |
| </source> |
| |
| <h3>Bind Mappings</h3> |
| |
| <p> |
| Also note the use of the "textBindMapping" attributes. Bind mappings allow a caller |
| to modify the data during the bind process. Incoming data can be converted before it |
| is assigned to a property, and outgoing data can be converted before it is stored in |
| the bind context. For example, the <tt>ValueMapping</tt> class formats incoming data |
| as a currency value in U.S. dollars: |
| </p> |
| |
| <source type="java"> |
| <![CDATA[ |
| public class ValueMapping implements Label.TextBindMapping { |
| private static final DecimalFormat FORMAT = new DecimalFormat("$0.00"); |
| |
| @Override |
| public String toString(Object value) { |
| return Float.isNaN((Float)value) ? null : FORMAT.format(value); |
| } |
| |
| @Override |
| public Object valueOf(String text) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| ]]> |
| </source> |
| |
| <p> |
| The <tt>toString() method is called to perform the translation during a <tt>load()</tt> |
| operation</tt>. The <tt>valueOf()</tt> method would be called during <tt>store()</tt>, |
| but throws <tt>UnsupportedOperationException</tt> because <tt>store()</tt> is never |
| called by the Stock Tracker application. |
| </p> |
| </body> |
| </document> |