Advanced Usage of Fragment Component"

From Documentation
(Article structure)
 
Line 14: Line 14:
  
 
== Property Binding and Validation ==
 
== Property Binding and Validation ==
You can append @validator annotation when you map properties of the view model as properties of a fragment. It helps you to validate data before saving to the view model. If validation fails, the save process will be aborted and the data will stay unchanged.
+
You can append <code>@validator</code> annotation when you map properties of the view model as properties of a fragment. It helps you to validate data before saving to the view model. If validation fails, the save process will be aborted and the data will stay unchanged.
  
 
<source lang='xml' high='2'>
 
<source lang='xml' high='2'>
Line 22: Line 22:
 
</fragment>
 
</fragment>
 
</source>
 
</source>
* Line 2: Append a @validator after @bind annotation to apply a validator.
+
* Line 2: Append a <code>@validator</code> after <code>@bind</code> annotation to apply a validator.
 +
 
 +
You can retrieve validation messages and display them. All you need to do is define a new property of a fragment using <code>@bind</code> to map the invalid message, so it can be bound by HTML elements.
 +
 
 +
<source lang='xml' high='1,3,5'>
 +
<fragment viewModel="..." validationMessages="@id('vmsgs')"
 +
          prop1="@bind(vm.prop1) @validator(vm.validator1)"
 +
          prop1err="@bind(vmsgs['prop1'])"><![CDATA[
 +
    <input type="text" value="@bind(prop1)"/>
 +
    <span textContent="@load(prop1err)"/>
 +
]]></fragment>
 +
</source>
 +
* Line 1: Don't forget to initialize a validation message holder.
 +
* Line 3: Bind an invalid message to prop1err.
 +
* Line 5: Use <code>@load</code> to get the invalid message.
  
 
=== Self-Defined Message Keys ===
 
=== Self-Defined Message Keys ===
 +
You can get the invalid message by assigning a self-defined key as an alias. The following is an example code showing how to add an invalid message with a self-defined key in a validator.
 +
 +
<source lang='java' high='4,7'>
 +
public class RegExValidator extends AbstractValidator {
 +
    public void validate(ValidationContext ctx) {
 +
        String regex = (String) ctx.getValidatorArg("regex");
 +
        String key = (String) ctx.getValidatorArg("key");
 +
        Object value = ctx.getProperty().getValue();
 +
        if (value == null || !value.toString().matches(regex)) {
 +
            addInvalidMessage(ctx, key, "Invalid: " + value);
 +
        }
 +
    }
 +
}
 +
</source>
 +
* Line 4: Get a key from the arguments of a validator.
 +
* Line 7: Use <code>addInvalidMessage(ValidationContext ctx, String key, String message)</code> to add a message with a key.
 +
 +
Then we can use it just like this following example.
 +
 +
<source lang='xml' high='2'>
 +
<fragment viewModel="..." validationMessages="@id('vmsgs')"
 +
            prop1="@bind(vm.prop1) @validator(vm.regExValidator, key='prop1', regex='^\\d+$')"
 +
            prop1err="@bind(vmsgs['prop1'])">
 +
    <input type="text" value="@bind(prop1)"/>
 +
    <span textContent="@load(prop1err)"/>
 +
</fragment>
 +
</source>
 +
* Line 2: Pass arguments (key, regex) to the validator.
  
 
== Form Binding and Validation ==
 
== Form Binding and Validation ==
 +
You can use [http://books.zkoss.org/zk-mvvm-book/8.0/data_binding/form_binding.html Form Binding] and form validators to validate all fields once and for all. The following example code is a fragment which is applied form binding, added a form validator and showed corresponding invalid messages if any.
 +
 +
<source lang='xml' high='2,3'>
 +
<fragment viewModel="..." validationMessages="@id('vmsgs')"
 +
            form="@id('fx') @load(vm) @save(vm, before='submit') @validator(vm.formValidator)"
 +
            prop1="@bind(fx.prop1)" prop1err="@bind(vmsgs['fkey1'])"
 +
            prop2="@bind(fx.prop2)" prop2err="@bind(vmsgs['fkey2'])"><![CDATA[
 +
    <p><input type="text" value="@bind(prop1)"/><span textContent="@load(prop1err)"/></p>
 +
    <p><input type="text" value="@bind(prop2)"/><span textContent="@load(prop2err)"/></p>
 +
    <button onclick="@command('submit')">Submit</button>
 +
]]></fragment>
 +
</source>
 +
* Line 2: Apply form binding by assigning form property to a component.
 +
* Line 3: Use the form middle object <code>fx</code> instead of ViewModel.
  
 
== JSR 303 Bean Validation ==
 
== JSR 303 Bean Validation ==
 +
ZK provides built-in validators. Both BeanValidator and FormBeanValidator integrate the Bean Validation 1.0 (JSR 303). For the introduction and environment setup please find the ZK MVVM Book: [http://books.zkoss.org/zk-mvvm-book/8.0/data_binding/validator.html#using-a-builtin-validator Using a Built-in Validator]. The following example code uses beanValidator and formBeanValidator to validate a JavaBean that is annotated with some constraints.
 +
 +
'''ViewModel'''
 +
<source lang='java'>
 +
public class DemoVM {
 +
    private SomeBean someBean = new SomeBean();
 +
    // getter and setter are omitted
 +
}
 +
</source>
 +
 +
'''JavaBean'''
 +
<source lang='java' high='4'>
 +
public class SomeBean {
 +
    private String prop1;
 +
 +
    @Size(min = 3, message = "The prop1 is too short (minimum is 3 characters)")
 +
    public String getProp1() {
 +
        return this.prop1;
 +
    }
 +
    public void setProp1(String prop1) {
 +
        this.prop1 = prop1;
 +
    }
 +
}
 +
</source>
 +
* Line 4: Use annotations that JSR 303 provided to define a constraint.
 +
 +
'''ZUML'''
 +
<source lang='xml' high='3'>
 +
<!-- property binding validation -->
 +
<fragment viewModel="..." validationMessages="@id('vmsgs')"
 +
            prop1="@bind(vm.someBean.prop1) @validator('beanValidator', key='fkey1')"
 +
            prop1err="@bind(vmsgs['fkey1'])"><![CDATA[
 +
    <input type="text" value="@bind(prop1)"/>
 +
    <span textContent="@load(prop1err)"/>
 +
]]></fragment>
 +
</source>
 +
* Line 3: Use the predefined <code>beanValidator</code> to validate property <code>prop1</code>. We can assign a custom message key by using key argument.
 +
 +
<source lang='xml' high='3'>
 +
<!-- form binding validation -->
 +
<fragment viewModel="..." validationMessages="@id('vmsgs')"
 +
        form="@id('fx') @load(vm.someBean) @save(vm.someBean, before='submit') @validator('formBeanValidator', prefix='p_')"
 +
        prop1="@bind(fx.prop1)"
 +
        prop1err="@bind(vmsgs['p_prop1'])"><![CDATA[
 +
    <input type="text" value="@bind(prop1)"/>
 +
    <span textContent="@load(prop1err)"/>
 +
    <button onclick="@command('submit')">Submit</button>
 +
]]></fragment>
 +
</source>
 +
* Line 3: Use the predefined <code>formBeanValidator</code> to validate a form.
  
 
== Client-Side Property Validation ==
 
== Client-Side Property Validation ==
 +
The fragment component provides a novel validator called <code>@jsvalidator</code> running at client side, accepting custom JavaScript functions for validation. The benefit is there is no need to make requests to the server each validation. Notice that the validation logic will be exposed at client side, so doing some simple check such as empty checking or range checking is recommended. The usage is like <code>@validator</code> but it is effective only on applying to HTML elements. The text inside the parentheses is the name of the referred validation function. There are two parameters passed on the validation function. The first one is the user input, and the second one is a validation message holder object. And the validation function is expected to return a Boolean value to tell the component if the data is valid. Only the valid data will be sent to the server for further operations.
 +
 +
<source lang='xml' high='2,5,6,7,8,9'>
 +
<fragment viewModel="..." prop1="@bind(vm.prop1)"><![CDATA[
 +
    <input type="text" value="@bind(prop1) @jsvalidator('validation')"/>
 +
    <span textContent="@load(vmsgs['prop1'])"/>
 +
    <script type="text/javascript">
 +
    function validation(val, vmsgs) {
 +
        var valid = doValidation(val);
 +
        vmsgs['prop1'] = valid ? '' : 'prop1 error';
 +
        return valid;
 +
    }
 +
 +
    function doValidation(val) {
 +
        // omitted
 +
    }
 +
    </script>
 +
]]></fragment>
 +
</source>
 +
* Line 2: Apply <code>@jsvalidator</code> directly to HTML elements. And use the declared <code>validation</code> function as a validation function.
 +
* Line 5-9: The <code>validation</code> function declared here. The logic is very simple.
  
 
=== The Differences Between @validator and @jsvalidator ===
 
=== The Differences Between @validator and @jsvalidator ===
 +
<div style="margin-left:auto;margin-right:auto;width:70%;">
 +
{| border="2" style="font-size:18; padding:10px;"
 +
! Catalogue
 +
! @validator
 +
! @jsvalidator
 +
|-
 +
| Validate at
 +
| Server side
 +
| Client side
 +
|-
 +
| ZK form validation
 +
| Supported
 +
| Not supported
 +
|-
 +
| Validation message holder
 +
| Initialized in validationMessages
 +
| An implicit vmsgs object
 +
|}
 +
</div>
 +
# <code>@validator</code> relies on the server, <code>@jsvalidator</code> relies on the browser.
 +
# <code>@jsvalidator</code> doesn't support form validation.
 +
# The validation message holders are not the same. See the following section to know more.
 +
 +
For security concerns, we recommended to use <code>@validator</code> in most cases and choose <code>@jsvalidator</code> if the validation needs an instant feedback such as password strength.
  
 
=== Validation Message Holder on Client-Side ===
 
=== Validation Message Holder on Client-Side ===
 +
The <code>@jsvalidator</code> also supports validation messages. For convenience there is a predefined message holder object named <code>vmsgs</code> in each fragment component. This object accepts self-defined keys to get messages. You can retrieve this object from the second argument of a validation function. Then you can fill out or clear messages depending on the validation result.
 +
 +
You can use an implicit object (<code>vmsgs</code>) to get the client-side invalid message so you don't need to either initialize a validationMessages holder object at the fragment tag or bind the invalid messages first to use. Notice that the holder object is not shared with server side.
 +
 +
<source lang='xml' high='3'>
 +
<fragment viewModel="..." prop1="@bind(vm.prop1)"><![CDATA[
 +
    // omitted
 +
    <span textContent="@load(vmsgs['prop1'])"/>
 +
    // omitted
 +
]]></fragment>
 +
</source>
 +
* Line 3: Use the <code>vmsgs</code> directly.
  
 
= Event Handling =
 
= Event Handling =
 +
You can attach DOM events on HTML elements to trigger commands of view models. The command of ViewModel will be executed and receive corresponding event objects.
 +
 +
Following is a quick lookup table of the relation between supported DOM events and ZK Event objects.
 +
{| border="2" class="wikitable"
 +
! ZK Event object
 +
! DOM event
 +
|-
 +
| rowspan="10" | MouseEvent
 +
| onclick
 +
|-
 +
| oncontextmenu
 +
|-
 +
| ondblclick
 +
|-
 +
| onmousedown
 +
|-
 +
| onmouseenter
 +
|-
 +
| onmouseleave
 +
|-
 +
| onmouseover
 +
|-
 +
| onmouseout
 +
|-
 +
| onmouseup
 +
|-
 +
| ondrag
 +
|-
 +
| rowspan="3" | KeyEvent
 +
| onkeydown
 +
|-
 +
| onkeypress
 +
|-
 +
| onkeyup
 +
|-
 +
| rowspan="2" | InputEvent
 +
| onchange
 +
|-
 +
| oninput
 +
|-
 +
| rowspan="2" | CheckEvent
 +
| onchange (checkbox)
 +
|-
 +
| oninput (checkbox)
 +
|-
 +
| SelectionEvent
 +
| onselect
 +
|-
 +
| DropEvent
 +
| ondrop
 +
|-
 +
| rowspan="4" | Event
 +
| onblur
 +
|-
 +
| onfocus
 +
|-
 +
| onfocusin
 +
|-
 +
| onfocusout
 +
|}
 +
 +
To retrieve an event object,  use a reserved keyword "event" in arguments of <code>@command</code> or apply annotation <code>@ContextParam(ContextType.TRIGGER_EVENT)</code> on a parameter of the command method. You can get detail here: [http://books.zkoss.org/zk-mvvm-book/8.0/advanced/parameters.html#retrieve-event-object Retrieve Event Object]. For properties of each event object, find out here: [https://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/event/package-frame.html org.zkoss.zk.ui.event Javadoc].
 +
 +
= Summary =
 +
In this article, we showed the interaction between the fragment component and the ZK MVVM mechanism. You can validate native HTML elements by using <code>@validator</code>, applying form binding, using built-in JSR 303 bean validators or trying a client-side <code>@jsvalidator</code>. And you can display the invalid messages after each validation.
 +
 +
In the second part we described the event object of the relative DOM events. You can get more detail from the event object such as mouse cursor position, pressed keys, entered text, and selected text.
  
= Summary =
+
The complete demo source code can be found in [https://github.com/zkoss-demo/zkfragment-demo-part2 GitHub].
  
 
= References =
 
= References =
 +
* [http://blog.zkoss.org/2016/11/15/client-binding-with-zk-mvvm-for-your-eyes-only/ Client Binding with ZK MVVM for your eyes only]
 +
* [http://books.zkoss.org/zk-mvvm-book/8.0/data_binding/validator.html ZK MVVM Reference - Validator]
 +
* [https://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/event/package-frame.html org.zkoss.zk.ui.event  Javadoc]
  
 
<references/>
 
<references/>

Revision as of 10:36, 12 June 2017

WarningTriangle-32x32.png This page is under construction, so we cannot guarantee the accuracy of the content!


DocumentationSmall Talks2017JulyAdvanced Usage of Fragment Component
Advanced Usage of Fragment Component

Author
Rudy Huang, Engineer, Potix Corporation
Date
June 12, 2017
Version
ZK 8.5

Introduction

The previous blog post "Client Binding with ZK MVVM for your eyes only" gives you a brief introduction of the Fragment component in upcoming ZK 8.5. This article will extend the introduction and explain advanced usage of this component especially about data validation and event handling.

Data Validation

To ensure data is correct and useful, we can accompany with validators. The fragment component supports property binding validation, form binding validation and client-side property validation available on this component only. This section explains them one by one.

Property Binding and Validation

You can append @validator annotation when you map properties of the view model as properties of a fragment. It helps you to validate data before saving to the view model. If validation fails, the save process will be aborted and the data will stay unchanged.

<fragment viewModel="..."
    prop1="@bind(vm.prop1) @validator(vm.validator1)">
    // omitted
</fragment>
  • Line 2: Append a @validator after @bind annotation to apply a validator.

You can retrieve validation messages and display them. All you need to do is define a new property of a fragment using @bind to map the invalid message, so it can be bound by HTML elements.

<fragment viewModel="..." validationMessages="@id('vmsgs')"
          prop1="@bind(vm.prop1) @validator(vm.validator1)"
          prop1err="@bind(vmsgs['prop1'])"><![CDATA[
    <input type="text" value="@bind(prop1)"/>
    <span textContent="@load(prop1err)"/>
]]></fragment>
  • Line 1: Don't forget to initialize a validation message holder.
  • Line 3: Bind an invalid message to prop1err.
  • Line 5: Use @load to get the invalid message.

Self-Defined Message Keys

You can get the invalid message by assigning a self-defined key as an alias. The following is an example code showing how to add an invalid message with a self-defined key in a validator.

public class RegExValidator extends AbstractValidator {
    public void validate(ValidationContext ctx) {
        String regex = (String) ctx.getValidatorArg("regex");
        String key = (String) ctx.getValidatorArg("key");
        Object value = ctx.getProperty().getValue();
        if (value == null || !value.toString().matches(regex)) {
            addInvalidMessage(ctx, key, "Invalid: " + value);
        }
    }
}
  • Line 4: Get a key from the arguments of a validator.
  • Line 7: Use addInvalidMessage(ValidationContext ctx, String key, String message) to add a message with a key.

Then we can use it just like this following example.

<fragment viewModel="..." validationMessages="@id('vmsgs')"
            prop1="@bind(vm.prop1) @validator(vm.regExValidator, key='prop1', regex='^\\d+$')"
            prop1err="@bind(vmsgs['prop1'])">
    <input type="text" value="@bind(prop1)"/>
    <span textContent="@load(prop1err)"/>
</fragment>
  • Line 2: Pass arguments (key, regex) to the validator.

Form Binding and Validation

You can use Form Binding and form validators to validate all fields once and for all. The following example code is a fragment which is applied form binding, added a form validator and showed corresponding invalid messages if any.

<fragment viewModel="..." validationMessages="@id('vmsgs')"
            form="@id('fx') @load(vm) @save(vm, before='submit') @validator(vm.formValidator)"
            prop1="@bind(fx.prop1)" prop1err="@bind(vmsgs['fkey1'])"
            prop2="@bind(fx.prop2)" prop2err="@bind(vmsgs['fkey2'])"><![CDATA[
    <p><input type="text" value="@bind(prop1)"/><span textContent="@load(prop1err)"/></p>
    <p><input type="text" value="@bind(prop2)"/><span textContent="@load(prop2err)"/></p>
    <button onclick="@command('submit')">Submit</button>
]]></fragment>
  • Line 2: Apply form binding by assigning form property to a component.
  • Line 3: Use the form middle object fx instead of ViewModel.

JSR 303 Bean Validation

ZK provides built-in validators. Both BeanValidator and FormBeanValidator integrate the Bean Validation 1.0 (JSR 303). For the introduction and environment setup please find the ZK MVVM Book: Using a Built-in Validator. The following example code uses beanValidator and formBeanValidator to validate a JavaBean that is annotated with some constraints.

ViewModel

public class DemoVM {
    private SomeBean someBean = new SomeBean();
    // getter and setter are omitted
}

JavaBean

public class SomeBean {
    private String prop1;

    @Size(min = 3, message = "The prop1 is too short (minimum is 3 characters)")
    public String getProp1() {
        return this.prop1;
    }
    public void setProp1(String prop1) {
        this.prop1 = prop1;
    }
}
  • Line 4: Use annotations that JSR 303 provided to define a constraint.

ZUML

<!-- property binding validation -->
<fragment viewModel="..." validationMessages="@id('vmsgs')"
            prop1="@bind(vm.someBean.prop1) @validator('beanValidator', key='fkey1')"
            prop1err="@bind(vmsgs['fkey1'])"><![CDATA[
    <input type="text" value="@bind(prop1)"/>
    <span textContent="@load(prop1err)"/>
]]></fragment>
  • Line 3: Use the predefined beanValidator to validate property prop1. We can assign a custom message key by using key argument.
<!-- form binding validation -->
<fragment viewModel="..." validationMessages="@id('vmsgs')"
        form="@id('fx') @load(vm.someBean) @save(vm.someBean, before='submit') @validator('formBeanValidator', prefix='p_')"
        prop1="@bind(fx.prop1)"
        prop1err="@bind(vmsgs['p_prop1'])"><![CDATA[
    <input type="text" value="@bind(prop1)"/>
    <span textContent="@load(prop1err)"/>
    <button onclick="@command('submit')">Submit</button>
]]></fragment>
  • Line 3: Use the predefined formBeanValidator to validate a form.

Client-Side Property Validation

The fragment component provides a novel validator called @jsvalidator running at client side, accepting custom JavaScript functions for validation. The benefit is there is no need to make requests to the server each validation. Notice that the validation logic will be exposed at client side, so doing some simple check such as empty checking or range checking is recommended. The usage is like @validator but it is effective only on applying to HTML elements. The text inside the parentheses is the name of the referred validation function. There are two parameters passed on the validation function. The first one is the user input, and the second one is a validation message holder object. And the validation function is expected to return a Boolean value to tell the component if the data is valid. Only the valid data will be sent to the server for further operations.

<fragment viewModel="..." prop1="@bind(vm.prop1)"><![CDATA[
    <input type="text" value="@bind(prop1) @jsvalidator('validation')"/>
    <span textContent="@load(vmsgs['prop1'])"/>
    <script type="text/javascript">
    function validation(val, vmsgs) {
        var valid = doValidation(val);
        vmsgs['prop1'] = valid ? '' : 'prop1 error';
        return valid;
    }

    function doValidation(val) {
        // omitted
    }
    </script>
]]></fragment>
  • Line 2: Apply @jsvalidator directly to HTML elements. And use the declared validation function as a validation function.
  • Line 5-9: The validation function declared here. The logic is very simple.

The Differences Between @validator and @jsvalidator

Catalogue @validator @jsvalidator
Validate at Server side Client side
ZK form validation Supported Not supported
Validation message holder Initialized in validationMessages An implicit vmsgs object
  1. @validator relies on the server, @jsvalidator relies on the browser.
  2. @jsvalidator doesn't support form validation.
  3. The validation message holders are not the same. See the following section to know more.

For security concerns, we recommended to use @validator in most cases and choose @jsvalidator if the validation needs an instant feedback such as password strength.

Validation Message Holder on Client-Side

The @jsvalidator also supports validation messages. For convenience there is a predefined message holder object named vmsgs in each fragment component. This object accepts self-defined keys to get messages. You can retrieve this object from the second argument of a validation function. Then you can fill out or clear messages depending on the validation result.

You can use an implicit object (vmsgs) to get the client-side invalid message so you don't need to either initialize a validationMessages holder object at the fragment tag or bind the invalid messages first to use. Notice that the holder object is not shared with server side.

<fragment viewModel="..." prop1="@bind(vm.prop1)"><![CDATA[
    // omitted
    <span textContent="@load(vmsgs['prop1'])"/>
    // omitted
]]></fragment>
  • Line 3: Use the vmsgs directly.

Event Handling

You can attach DOM events on HTML elements to trigger commands of view models. The command of ViewModel will be executed and receive corresponding event objects.

Following is a quick lookup table of the relation between supported DOM events and ZK Event objects.

ZK Event object DOM event
MouseEvent onclick
oncontextmenu
ondblclick
onmousedown
onmouseenter
onmouseleave
onmouseover
onmouseout
onmouseup
ondrag
KeyEvent onkeydown
onkeypress
onkeyup
InputEvent onchange
oninput
CheckEvent onchange (checkbox)
oninput (checkbox)
SelectionEvent onselect
DropEvent ondrop
Event onblur
onfocus
onfocusin
onfocusout

To retrieve an event object, use a reserved keyword "event" in arguments of @command or apply annotation @ContextParam(ContextType.TRIGGER_EVENT) on a parameter of the command method. You can get detail here: Retrieve Event Object. For properties of each event object, find out here: org.zkoss.zk.ui.event Javadoc.

Summary

In this article, we showed the interaction between the fragment component and the ZK MVVM mechanism. You can validate native HTML elements by using @validator, applying form binding, using built-in JSR 303 bean validators or trying a client-side @jsvalidator. And you can display the invalid messages after each validation.

In the second part we described the event object of the relative DOM events. You can get more detail from the event object such as mouse cursor position, pressed keys, entered text, and selected text.

The complete demo source code can be found in GitHub.

References



Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.