Create Data Binding Programmatically"

From Documentation
Line 12: Line 12:
  
  
 +
Assume we have a form to fill in personal information.
 +
 +
<source lang="xml">
 +
<window apply="org.zkoss.reference.developer.mvvm.advance.DynamicBindingComposer" width="600px">
 +
<grid >
 +
<rows>
 +
<row>
 +
First Name:
 +
<textbox id="fn"/>
 +
</row>
 +
<row>
 +
Last Name:
 +
<textbox id="ln"/>
 +
</row>
 +
<row>
 +
Age:
 +
<intbox/>
 +
</row>
 +
<row spans="2">
 +
<div>
 +
<button label="Submit" />
 +
<button label="Reset" />
 +
</div>
 +
</row>
 +
<row spans="2">
 +
<div>
 +
Preview: I am <label id="fnLabel" /> <label id="lnLabel" />, <label id="ageLabel"/> years old.
 +
</div>
 +
</row>
 +
</rows>
 +
</grid>
 +
...
 +
</window>
 +
</source>
 +
 +
We hope that user input can be automatically saved to a bean.
  
 
'''3 basic steps example'''
 
'''3 basic steps example'''
<source lang="java" high='3,10,12,14,15, 18'>
+
<source lang="java" high='3,11,13,15,17, 21'>
 
public class DynamicBindingComposer extends SelectorComposer {
 
public class DynamicBindingComposer extends SelectorComposer {
  
 
private Binder binder = new DefaultBinder();
 
private Binder binder = new DefaultBinder();
 
...
 
...
 +
private Person person;
  
 
@Override
 
@Override
Line 24: Line 61:
 
super.doAfterCompose(comp);
 
super.doAfterCompose(comp);
 
 
binder.init(grid,this, null);
+
binder.init(comp,this, null);
 
 
comp.setAttribute("person", person);
+
grid.setAttribute("person", person);
 
 
binder.addPropertySaveBindings(firstNameBox, "value", "person.firstName", null, null, null, null, null,null,null);
+
binder.addPropertySaveBindings(firstNameBox, "value", "person.firstName"
binder.addPropertyLoadBindings(firstNameBox, "value", "person.firstName", null, null, null, null, null);
+
, null, null, null, null, null,null,null);
 +
binder.addPropertyLoadBindings(firstNameBox, "value", "person.firstName"
 +
, null, null, null, null, null);
 
// add more data bindings...
 
// add more data bindings...
 
 
Line 36: Line 75:
 
</source>
 
</source>
 
* Line 3: Create a <javadoc>org.zkoss.bind.DefaultBinder</javadoc> to use as its Javadoc suggests.
 
* Line 3: Create a <javadoc>org.zkoss.bind.DefaultBinder</javadoc> to use as its Javadoc suggests.
* Line 10: We should initialize <tt>DefaultBinder</tt> before using it.
+
* Line 11: We should initialize <tt>DefaultBinder</tt> before using it. The first parameter is root component. The second parameter is ViewModel object. In this example, the composer plays the role as a ViewModel.
* Line 12:
+
* Line 13: Set the data bean as an attribute of the ''Grid'', this can make the bean be accessible for EL expression by its key: <tt>person</tt>.
* Line 14:
+
* Line 15,17: Add save- or load-binding according to our requirement.
* Line 15:
+
* Line 21: Load data for all load-binding inside the component specified at first parameter which is a ''Grid''.
* Line 18:
 
  
  
Line 56: Line 94:
 
binder.addPropertyLoadBindings(firstNameBox, "value", "vm.person.firstName", null, null, null, null, null);
 
binder.addPropertyLoadBindings(firstNameBox, "value", "vm.person.firstName", null, null, null, null, null);
 
binder.addPropertySaveBindings(firstNameBox,"value","vm.person.firstName", null, null, null, null, null,null,null);
 
binder.addPropertySaveBindings(firstNameBox,"value","vm.person.firstName", null, null, null, null, null,null,null);
</source>
 
 
 
 
<source lang="java">
 
String[] command = {"submit"};
 
binder.addPropertyLoadBindings(firstNameLabel, "value", "vm.person.firstName", null, command, null, null, null);
 
</source>
 
 
 
== Form Binding ==
 
 
<source lang="java">
 
binder.addFormLoadBindings(grid, "fx", "vm.person", null, null, null);
 
binder.addFormSaveBindings(grid, "fx", "vm.person", command, null, null, null, null);
 
</source>
 
 
 
<source lang="java">
 
binder.addPropertyLoadBindings(firstNameBox, "value", "fx.firstName", null, null, null, null, null);
 
binder.addPropertySaveBindings(firstNameBox, "value", "fx.firstName", null, null, null, null, null
 
,"vm.emptyValidator",null);
 
</source>
 
 
<source lang="xml" high="3,8">
 
<div apply="org.zkoss.bind.BindComposer"
 
viewModel="@id('vm') @init('org.zkoss.reference.developer.mvvm.advance.DynamicFormBindingVM')"
 
validationMessages="@id('vmsgs')">
 
...
 
<row>
 
First Name:
 
<textbox id="fn"/>
 
<label id="fnError" style="color:red" />
 
</row>
 
</source>
 
 
 
<source lang="java">
 
binder.addPropertyLoadBindings(fNameErrorLabel, "value", "vmsgs[fn]", null, null, null, null, null);
 
 
</source>
 
</source>
  

Revision as of 04:29, 2 January 2013

Overview

Under MVC approach, getting component's attribute value by calling getter methods causes lots of routine code in a composer. But under MVVM approach, all attributes' values are saved to ViewModel's properties automatically without calling any methods because of data binding. Through using a binder to add data binding for components, we also can enjoy this benefit in a composer. This section introduces basic usages of a binder.

Binder API Usage

There are 3 basic steps to create data bindings:

  1. Initialize the binder
  2. Store data objects as attributes
    To make data be available for EL expressions
  3. Add data bindings


Assume we have a form to fill in personal information.

	<window apply="org.zkoss.reference.developer.mvvm.advance.DynamicBindingComposer" width="600px">
		<grid >
			<rows>
				<row>
					First Name:
					<textbox id="fn"/>
				</row>
				<row>
					Last Name:
					<textbox id="ln"/>
				</row>
				<row>
					Age:
					<intbox/>
				</row>
				<row spans="2">
					<div>
						<button label="Submit" />
						<button label="Reset" />
					</div>
				</row>
				<row spans="2">
					<div>
					Preview: I am <label id="fnLabel" /> <label id="lnLabel" />, <label id="ageLabel"/> years old.
					</div>
				</row>
			</rows>
		</grid>
		...
	</window>

We hope that user input can be automatically saved to a bean.

3 basic steps example

public class DynamicBindingComposer extends SelectorComposer {

	private Binder binder = new DefaultBinder();
	...
	private Person person;

	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		binder.init(comp,this, null);
		
		grid.setAttribute("person", person);
		
		binder.addPropertySaveBindings(firstNameBox, "value", "person.firstName"
				, null, null, null, null, null,null,null);
		binder.addPropertyLoadBindings(firstNameBox, "value", "person.firstName"
				, null, null, null, null, null);
		// add more data bindings...
		
		binder.loadComponent(grid, false); //load beans' data to initialize components
	}
  • Line 3: Create a DefaultBinder to use as its Javadoc suggests.
  • Line 11: We should initialize DefaultBinder before using it. The first parameter is root component. The second parameter is ViewModel object. In this example, the composer plays the role as a ViewModel.
  • Line 13: Set the data bean as an attribute of the Grid, this can make the bean be accessible for EL expression by its key: person.
  • Line 15,17: Add save- or load-binding according to our requirement.
  • Line 21: Load data for all load-binding inside the component specified at first parameter which is a Grid.


Here we give some common examples, for more details please refer to Javadoc: Binder.


Property binding in a ZUL

<textbox value="@bind(vm.person.firstName)"/>


Programmatic property binding

binder.addPropertyLoadBindings(firstNameBox, "value", "vm.person.firstName", null, null, null, null, null);
binder.addPropertySaveBindings(firstNameBox,"value","vm.person.firstName", null, null, null, null, null,null,null);

Add Data Binding for Collections

Assume that we hope end users can edit an item directly in a Listbox. Hence we could put a Textbox in each Listcell and make the Textbox bind to a object of Listbox's model. This data binding cannot be made by writing annotation in a zul because those Textboxs are dynamically created.

	<window apply="org.zkoss.reference.developer.mvvm.advance.DynamicCollectionBindingComposer"
		width="600px">
		...
		<div apply="org.zkoss.bind.BindComposer"
			viewModel="@id('vm') @init('org.zkoss.reference.developer.mvvm.advance.DynamicCollectionBindingVM')">
			<listbox model="@load(vm.personList)" selectedItem="@bind(vm.selectedPerson)" rows="10">

			</listbox>
			...
		</div>
		...
	</window>


Use ItemRenderer

Dev-ref-mvvm-adv-data-binding-programmatically.png


	class MyListboxRenderer implements ListitemRenderer{

		public void render(Listitem listitem, Object data, int index)
				throws Exception {

			//TODO explain why
			listitem.setAttribute("each", data);
			
			//first name
			Listcell fnCell = new Listcell();
			listitem.appendChild(fnCell);
			Textbox fnBox = new Textbox();
			fnBox.setInplace(true);
			fnCell.appendChild(fnBox);
			binder.addPropertyLoadBindings(fnBox, "value", "each.firstName", null, null, null, null, null);
			binder.addPropertySaveBindings(fnBox, "value", "each.firstName", null, null, null, null, null, null, null);

			//last name
			...

			//age
			...

			//delete button
			...
		}
		
	}

Use Template