Wiki Wiki

« Zpět na FrontPage

DataBinding enhacements

Štítky: zk zk-dl databinding

We did several enhancements to ZK's databinding. We hope to see them as a part of ZK's one day - we will create feature requests.

Bean validation (JSR 303)#

See the - Hibernate Validator for more information about bean validation. Basically, this use javax.validation.constraints annotations to declaratively create validations.
 /** Name of the category */
    private String name;

    /** When is the due of this task */
    @Temporal(value = TemporalType.TIMESTAMP)
    @Future(message = "The task due must be set into future.")
    private Date taskDue;

Hibernate Validator attached itself to JPA and fire the validation before database insert/update. But we need fire the validation earlier - ideally in AJAX call after the user fills according text filed. This validation is part of JSF and other frameworks.

Validation factory#

The validation factory is already part of the standard - just call javax.validation.Validation.buildDefaultValidatorFactory() method. The Validation interface is then available via method getValidation() on this factory.

We use Spring configuration to create validator bean:

 <!-- validator for bean validation -->
        <bean id="validationFactory" class="javax.validation.Validation" factory-method="buildDefaultValidatorFactory"/>
        <bean id="validator" factory-bean="validationFactory" factory-method="getValidator" />

Attach the validation to DataBinding#

We created new property validator of the DataBinder class. You can set it directly (via setter, or conveniently via AnnotateDataBinderInit.
 <?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" validator="${validator}"?>


The validation is part of DataBinder itself, you don't need to do anything:

Part of DataBinder source code - setBeanAndRegisterBeanSameNodes:

                // DTL JB - bean validation
                ValidatorAdapter validator = getValidator();
                if (validator != null)
                    validator.validate(comp, bean, beanid, val);

		Fields.set(bean, beanid, val, autoConvert);
Before the field is actually set, we do a bean validation.

As you can see, we use ValidatorAdapter, which translates to the bean validation API. The implementation is pretty straightforward. Just validate and if anything fails, throw the ZK's WrongValueException.

* Validate component comp - bind the validateValue to property beanid of object bean.
* @param comp UI component with new value
* @param bean bean to set value
* @param beanid property of the bean
* @param validateValue new value (to validate)
    public void validate(Component comp, Object bean, String beanid, Object validateValue)
        if (!validator.getConstraintsForClass(bean.getClass()).isBeanConstrained())

        // translate empty String to null - ZK sends always empty string instead of null for field and @NotNull doesn't work
        if (validateValue != null && validateValue instanceof String && ((String)validateValue).length() == 0)
            validateValue = null;

        Set constraintViolations = validator.validateValue(bean.getClass(), beanid, validateValue);

        if (constraintViolations.size() > 0)
            List<WrongValueException> exceptions = new LinkedList();
            for (ConstraintViolation violation : (Set<ConstraintViolation>)constraintViolations)
                exceptions.add(new WrongValueException(comp, violation.getMessage()));
            throw new WrongValuesException(exceptions.toArray(new WrongValueException[] {}));

When the validation fires?#

Short answer is - before the value is set by ZK's databinding mechanism :)

Ok, see ZK's documentation regarding when this happens. Usually it is on some event like onChange or onSelect. For a textbox, it fires directly after you loose the focus of a textbox. Because the validation throws ZK's WrongValueException, it is translated to a AJAX response and a nice message is shown near the textbox.

There is one problem, @NotNull validation will not fire, if you do not change any value in a component. To solve this, just call saveAll() on the DataBinder on some "Ok" button:

* Save the item.
    @ZkEvents(events = {
            @ZkEvent(id = "saveCategoryButton"),
            @ZkEvent(event = Events.ON_OK)
    @ZkBinding(saveBefore = true, loadAfter = false)
    public void save() {;
        ZKHelper.closeDetailWindow(self, true, category);
You can use @ZkBinding(saveBefore = true, loadAfter = false) annotation from our ZKComposer.

Converter in Composer#

You can customize the conversion between data source and UI component via ZK's TypeConverter. This is universal method, but there are two problems:
  • you need to create separate class for each conversion
  • you don't have access to controller properties

We created an implementation of this TypeConverter interface - see org.zkoss.zkplus.databind.MethodTypeConverter. When you use converter like this:

 <listcell label="@{person.married, converter='ctl.myMarriedConverter'}" />
the bidning will first try to find a class ctl.myMarriedConverter. If no such class exists, it will parse the expression to find "ctl" variable. If it exists, it will try to invoke a method myMarriedConverter on it.

How to use our binding?#

You need to exclude original ZK's zkplus.jar, and attach our zkplus instead.

You can download it from Google code downloads or use our maven repository.

Maven setup#

You need to exclude default zkplus from our components and include custom zkplus.

            <!-- Same as ZK's zkplus, with extensions - bean validator + converter -->
This configuration is part of our maven archetype.
0 Přílohy
9727 Zobrazení
Průměr (0 Hlasů)