Friday, April 26, 2013

ZK MVVM: Form Binding with Validator


Introduction

This article describe how to use Validator with Form Binding in ZK MVVM.

The Result

View demo on line
http://screencast.com/t/caPtmte73J

Pre-request

ZK MVVM: Form Binding
http://ben-bai.blogspot.tw/2013/04/zk-mvvm-form-binding.html

The Program

MVVM_FormBinding.zul

The inner div bind to vm.person, i.e., use vm.person as a form object. It will save form content before savePerson command and use vm.formValidator as the validator to do validation of form fields.

The validator will store error message in validationMessages map with the specified id 'vmsgs'

<zk>
    <!-- 
        tested with ZK 6.0.2

        validationMessages="@id('vmsgs')"
        means all validation message will be stored in a map
        that mapped by the id 'vmsgs'

        i.e., you can get messages from that map by 
        @bind(vmsgs['keyOfMessageContent'])
    -->
    <div apply="org.zkoss.bind.BindComposer"
        viewModel="@id('vm') @init('blog.ben.test.mvvm.formbinding.FormBindingWithValidatorTestVM')"
        validationMessages="@id('vmsgs')">
        <!-- 
            @validator(vm.formValidator)
            do validation before save form with this validator
         -->
        <div form="@id('fx') @load(vm.person) @save(vm.person, before='savePerson') @validator(vm.formValidator)">
            <vlayout>
                <hlayout>
                    <label value="First Name" />
                    <textbox value="@bind(fx.firstName)" />
                    <!-- 
                        @bind(vmsgs['firstNameContentError'])
                        show error message of the key 'firstNameContentError' in
                        vmsgs map after form validation as needed
                     -->
                    <label value="@bind(vmsgs['firstNameContentError'])" style="color: red;" />
                    <label value="@bind(vmsgs['firstNameError'])" style="color: red;" />
                </hlayout>
                <hlayout>
                    <label value="Last Name" />
                    <textbox value="@bind(fx.lastName)" />
                    <label value="@bind(vmsgs['lastNameContentError'])" style="color: red;" />
                    <label value="@bind(vmsgs['lastNameError'])" style="color: red;" />
                </hlayout>
                <hlayout>
                    <label value="Age" />
                    <intbox value="@bind(fx.age)" />
                    <label value="@bind(vmsgs['ageContentError'])" style="color: red;" />
                    <label value="@bind(vmsgs['ageTooSmallError'])" style="color: red;" />
                    <label value="@bind(vmsgs['ageTooLargeError'])" style="color: red;" />
                </hlayout>
            </vlayout>
        </div>
        <button label="save" onClick="@command('savePerson')" />
        <label value="@load(vm.personContent)" />
    </div>
</zk>


FormBindingTestVM.java

Simple vm, contains getter, command and a validator

package blog.ben.test.mvvm.formbinding;

import org.zkoss.bind.ValidationContext;
import org.zkoss.bind.Validator;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.bind.validator.AbstractValidator;

/**
 * tested with ZK 6.0.2
 * @author benbai123
 *
 */
public class FormBindingWithValidatorTestVM {
    private Person _person;
    public Person getPerson () {
        if (_person == null) {
            _person = new Person("Ben", "Bai", 123); // fake
        }
        return _person;
    }
    public String getPersonContent () {
        return _person.getFirstName() + " " + _person.getLastName() + ", age = " + _person.getAge();
    }
    /**
     * empty function as a command for
     * save in form binding and update personContent
     */
    @Command
    @NotifyChange("personContent")
    public void savePerson () {
    }
    /**
     * get the validator that will do validation while save
     * @return
     */
    public Validator getFormValidator(){
        return new AbstractValidator() {

            public void validate(ValidationContext ctx) {
                // get value from form context,
                // ctx.getProperties("firstName")[0].getValue() will
                // get the value that bind with @bind(fx.firstName)
                // in zul page
                String firstName = (String)ctx.getProperties("firstName")[0].getValue();
                String lastName = (String)ctx.getProperties("lastName")[0].getValue();
                Integer age = (Integer)ctx.getProperties("age")[0].getValue();
                if (firstName == null || firstName.isEmpty()) {
                    // put error message into validationMessages map
                    addInvalidMessage(ctx, "firstNameContentError", "firstName is required ");
                } else if (firstName.length() < 3) {
                    addInvalidMessage(ctx, "firstNameError", "firstName at least 3 chars ");
                }
                if (lastName == null || lastName.isEmpty()) {
                    addInvalidMessage(ctx, "lastNameContentError", "lastName is required");
                } else if (lastName.length() < 3) {
                    addInvalidMessage(ctx, "lastNameError", "lastName at least 3 chars ");
                }
                if (age == null) {
                    addInvalidMessage(ctx, "ageContentError", "age is required");
                } else {
                    if (age < 0) {
                        addInvalidMessage(ctx, "ageTooSmallError", "age should not be negative");
                    }
                    if (age > 130) {
                        addInvalidMessage(ctx, "ageTooLargeError", "age should smaller than 130");
                    }
                }
            }
        };
    }
}


Person.java

Simple pojo contains data of a person

package blog.ben.test.mvvm.formbinding;

public class Person {
    String _firstName;
    String _lastName;
    Integer _age;
    public Person (String firstName, String lastName, Integer age) {
        _firstName = firstName;
        _lastName = lastName;
        _age = age;
    }
    public void setFirstName (String firstName) {
        _firstName = firstName;
    }
    public void setLastName (String lastName) {
        _lastName = lastName;
    }
    public void setAge (int age) {
        _age = age;
    }
    public String getFirstName () {
        return _firstName;
    }
    public String getLastName () {
        return _lastName;
    }
    public Integer getAge () {
        return _age;
    }
}


Reference

MVVM > Data Binding > Validator
http://books.zkoss.org/wiki/ZK_Developer's_Reference/MVVM/Data_Binding/Validator

Download

MVVM_FormBinding_with_Validator.zul
https://github.com/benbai123/ZK_Practice/blob/master/Pattern/MVVM/AdvancedMVVM/WebContent/MVVM_FormBinding_with_Validator.zul

FormBindingWithValidatorTestVM.java
https://github.com/benbai123/ZK_Practice/blob/master/Pattern/MVVM/AdvancedMVVM/src/blog/ben/test/mvvm/formbinding/FormBindingWithValidatorTestVM.java

Person.java
https://github.com/benbai123/ZK_Practice/blob/master/Pattern/MVVM/AdvancedMVVM/src/blog/ben/test/mvvm/formbinding/Person.java

Demo Flash
https://github.com/benbai123/ZK_Practice/blob/master/demo_src/swf/Pattern/MVVM/MVVM_FormBinding_with_Validator.swf

No comments:

Post a Comment