Form Binding
This article is out of date, please refer to zk-mvvm-book/8.0/data_binding/form_binding for more up to date information.
Overview
Form binding is like a buffer. It automatically creates a middle object. Before saving to ViewModel all input data is saved to the middle object. In this way we can keep dirty data from saving into the ViewModel before user confirmation. Assuming a user fills a form in a web application, user input data is directly saved to ViewModel's properties, the target object. Then the user cancel the filling action before submitting the form, thus the data stored in ViewModel is deprecated. That would cause trouble if we further process the deprecated data, so developers might store input data in a middle place first then move to real target object after the user confirms it. Form binding provide a middle object to store unconfirmed input without implementing by yourself.
Form binding can keep target object in ViewModel unchanged before executing a Command for confirmation. Before saving to ViewModel's properties (target object) upon a Command, we can save input in Form binding's middle object. When the command is executed (e.g. button is clicked), input data is really saved to ViewModel's properties. Developers can achieve the whole process easily just by writing ZK bind expression and it reduces developer's burden of cleaning dirty data manually or implementing buffer himself.
The data flow among ZUL, middle object, and the target object is illustrated below:
Steps
- Give an id to middle object in form attribute with
@id
.- You can reference the middle object in ZK bind expression with id.
- Specify ViewModel's property to be loaded with
@load
- Specify ViewModel's property to save and before which Command with
@save
- This means binder will save middle object's properties to ViewModel before a Command
- Bind component's attribute to middle object's properties like you do in property binding.
- You should use middle object's id specified in
@id
to reference its property.
- You should use middle object's id specified in
An example using property binding :
<groupbox >
<grid hflex="true" >
<columns>
<column width="120px"/>
<column/>
</columns>
<rows>
<row>Id
<hlayout>
<label value="@load(vm.selected.id)"/>
</hlayout> </row>
<row>Description <textbox value="@bind(vm.selected.description)"/></row>
<row>Quantity
<intbox value="@bind(vm.selected.quantity)"/>
</row>
<row>Price
<doublebox value="@bind(vm.selected.price)" format="###,##0.00" />
</row>
</rows>
</grid>
</groupbox>
- The input data is directly saved to properties of vm.selected. No middle object.
The same example but using form binding:
<groupbox form="@id('fx') @load(vm.selected) @save(vm.selected, before='saveOrder')">
<grid hflex="true" >
<columns>
<column/>
<column/>
</columns>
<rows>
<row>Id
<hlayout>
<label value="@load(fx.id)"/>
</hlayout> </row>
<row>Description <textbox value="@bind(fx.description)"/></row>
<row>Quantity
<intbox value="@bind(fx.quantity) "/>
</row>
<row>Price
<doublebox value="@bind(fx.price)" format="###,##0.00" />
</row>
</rows>
</grid>
</groupbox>
- We give an id:
fx
to the middle object. (line 1) - The properties of fx we can access are identical to vm.selected. (line 12,14,17)
- Binder saves input data into fx, middle object, when each time a user triggers an onChange event. When clicking a button bound to Command 'saveOrder', it will save middle object's data to vm.selected.
Middle Object
Form binding automatically creates a middle object for you to store properties from ViewModel's object you specified. But it only stores those properties which attributes are bound to. If you still need to access a property that are not stored in the middle object, you can access it from ViewModel's original object. Assume that vm.selected
has 4 properties: id, description, quantity, price. For the example below:
<groupbox form="@id('fx') @load(vm.selected) @save(vm.selected, before='saveOrder')">
<grid hflex="true" >
<rows>
<row>Id <label value="@load(fx.id)"/>
</row>
<row>Description <textbox value="@bind(fx.description)"/>
</row>
<row>Quantity <intbox value="@bind(fx.quantity) "/>
</row>
</rows>
</grid>
</groupbox>
- Because we only bind attributes to 3 properties:"id", "description", and "quantity", the middle object only stores these 3 properties. Therefore the "price" property is not stored in middle object, we cannot reference it without binding it first. For example, we cannot pass it as parameter by
@command('cmd', currentPrice=fx.price)
, becausefx.price
doesn't exist in the middle object.
Form Status Variable
Form binding also records middle object's modification status.It’s a common requirement for users to know that whether they have modified a form’s data (dirty status) or not, developers therefore add a feature that would remind users of this with an UI effect. For example, some text editors appends a star symbol ‘*’ on the title bar to remind users of modified text file. "Form binding" preserves the dirty status in an variable that we can utilize it.
Dirty status is stored in an auto-created form status variable with a naming convention of:
[middleObjectId]Status
Continue above example, we add an exclamation icon right next to Id value. If users modify any input data, the ex exclamation icon will show up.
<groupbox form="@id('fx') @load(vm.selected) @save(vm.selected, before='saveOrder')" >
<grid hflex="true" >
<columns>
<column width="120px"/>
<column/>
</columns>
<rows>
<row>
Id
<hlayout>
<label value="@load(fx.id)" />
<image src="@load(fxStatus.dirty?'exclamation.png':'')" />
</hlayout>
</row>
<row>Description <textbox value="@bind(fx.description)"/></row>
<row>Quantity
<intbox value="@bind(fx.quantity) "/>
</row>
<row>Price
<doublebox value="@bind(fx.price)" format="###,##0.00" />
</row>
<row>Total Price
<label value="@load(fx.totalPrice)" />
</row>
<row>Creation Date
<datebox value="@bind(fx.creationDate)"/>
</row>
<row>Shipping Date
<datebox value="@bind(fx.shippingDate)"/>
</row>
</rows>
</grid>
</groupbox>
- In this example, form status variable is
fxStatus
for the form’s id isfx
. Its dirty property indicates that whether the form has been modified by users or not.
After users modify a field, an exclamation icon shows up next to “Id” field. If users click “Save” button or change data back to original value, the exclamation icon disappears.
Initialize with Form Object
If you want to gain more control over form binding, e.g. to manipulate the middle object, you can provide an object which implements Form interface (or you can use SimpleForm for convenient) within @init()
. This will initialize form binding with your own middle object (Form object), then you can do whatever you want like notifying others when middle object's properties change.
Initialize with Form object
<groupbox form="@id('fx') @init(vm.myForm) @load(vm.selected) @save(vm.selected, before='saveOrder') " >
Prepare a Form object
public class OrderVM {
Form myForm = new SimpleForm();
public Form getMyForm() {
return myForm;
}
}
Form Validation
Before saving data to form's middle object, we also can validate user input with validator. Please refer to ZK Developer's Reference/MVVM/DataBinding/Validator.
Version History
Version | Date | Content |
---|---|---|
6.0.0 | February 2012 | The MVVM was introduced. |