Create Data Binding Programmatically"

From Documentation
Line 83: Line 83:
 
* Line 17: 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 17: 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 19: 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 19: 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 21,23: Add save- or load-binding according to our requirement.
+
* Line 21,23: Add save- or load-binding between the person bean's property <tt>person.firstName</tt> and the <tt>firstNameBox</tt>'s <tt>value</tt> attribute. There are many parameters that we don't use in this example, so we pass <tt>null</tt>.  please refer to Javadoc: <javadoc>org.zkoss.bind.Binder</javadoc> for more details.
 
* Line 27: Load data for all load-bindings inside the ''Grid'' which is specified at first parameter.
 
* Line 27: Load data for all load-bindings inside the ''Grid'' which is specified at first parameter.
  
  
Here we give some common examples, for more details please refer to Javadoc: <javadoc>org.zkoss.bind.Binder</javadoc>.
+
The codes to add data binding:
 
+
<source lang="java">
 +
binder.addPropertySaveBindings(firstNameBox, "value", "person.firstName"
 +
, null, null, null, null, null,null,null);
 +
binder.addPropertyLoadBindings(firstNameBox, "value", "person.firstName"
 +
, null, null, null, null, null);
 +
</source>
  
'''Property binding in a ZUL'''
+
They equals to the below data binding annotation in a ZUL:
 
<source lang="xml">
 
<source lang="xml">
 
<textbox value="@bind(vm.person.firstName)"/>
 
<textbox value="@bind(vm.person.firstName)"/>
Line 96: Line 101:
  
  
'''Programmatic property binding'''
+
After data binding is created, each time when we need to get user input, we can use the data bean directly instead of getting from a specific components as follows:
<source lang="java">
+
 
binder.addPropertyLoadBindings(firstNameBox, "value", "vm.person.firstName", null, null, null, null, null);
+
<source lang="java" high='7'>
binder.addPropertySaveBindings(firstNameBox,"value","vm.person.firstName", null, null, null, null, null,null,null);
+
public class DynamicBindingComposer extends SelectorComposer {
 +
 
 +
...
 +
 
 +
@Listen("onClick = button[label='Submit']")
 +
public void submit(){
 +
Messagebox.show("I am "+person.getFirstName()+" "
 +
+person.getLastName()+", "+person.getAge()+" years old.");
 +
}
 +
 
 +
...
 +
}
 
</source>
 
</source>
 +
 +
 +
== Re-load the Load-Binding ==
 +
 +
If we change the data bean, we could trigger the binder to reload those load-binding for us. Assume
  
 
= Add Data Binding for Collections=
 
= Add Data Binding for Collections=

Revision as of 07: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();

	@Wire("grid")
	private Grid grid;
	
	@Wire("#fn") 
	private Textbox firstNameBox;
	...
	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 17: 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 19: 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 21,23: Add save- or load-binding between the person bean's property person.firstName and the firstNameBox's value attribute. There are many parameters that we don't use in this example, so we pass null. please refer to Javadoc: Binder for more details.
  • Line 27: Load data for all load-bindings inside the Grid which is specified at first parameter.


The codes to add data binding:

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

They equals to the below data binding annotation in a ZUL:

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


After data binding is created, each time when we need to get user input, we can use the data bean directly instead of getting from a specific components as follows:

public class DynamicBindingComposer extends SelectorComposer {

	...

	@Listen("onClick = button[label='Submit']")
	public void submit(){
		Messagebox.show("I am "+person.getFirstName()+" "
			+person.getLastName()+", "+person.getAge()+" years old.");
	}

	...
}


Re-load the Load-Binding

If we change the data bean, we could trigger the binder to reload those load-binding for us. Assume

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