Wednesday, January 11, 2012

Sons of DHTMLX and Spring #2

Episode 2: Caregiver
In the previous episode the DHTMLX Spring Adatper was introduced, now it is time to take care of the object through validation. 



Forms
Since version 2.0 the Spring framework includes a JSP  tag library,  to make writing HTML forms much easier. A typical HTML form written using Spring JSP form tags  looks like the code below: 
Spring Form
  1. <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
  2. <form:form id="book" method="post">
  3.   <form:label path="author">Author:</form:label>
  4.   <form:input path="author"/> <br/>
  5.   <form:label path="title">Title:</form:label>
  6.   <form:input path="title"/> <br/>
  7.   <form:label path="price">Price:</form:label>
  8.   <form:input path="price"/> <br/>
  9.   <form:label path="sales">Sales:</form:label>
  10.   <form:input path="sales"/> <br/>
  11.   <input type="submit" value="Save">
  12. </form:form>
DHTMLX version 2.6 introduced the dhtmlxForm component in 2010, providing a flexible Javascript API to create rich form, process events and validate data. The version 3.0 released in July 2011 includes a new version of dhtmlxForm, supporting integration with dhtmlxCalendar and dhtmlxCombo, and new ways to position form controls to build sophisticated web forms. Below the  Javascript code to create a DHTMLX form:
DHTMLX Form
  1. <div id="form" style="width:280px;height:250px;"/>
  2. var form_struct = [
  3.   { type: 'input', name: 'author' , value: '', label:'Author:'},
  4.   { type: 'input', name: 'title' , value: '', label:'Title:'},
  5.   { type: 'input', name: 'price' , value: '', label:'Price:'},
  6.   { type: 'input', name: 'sales' , value: '', label:'Sales:'},
  7.   { type: 'button', name: 'button_save', value:'Save'  },
  8.   ];
  9. var formObject = new dhtmlXForm("form",form_struct);
The DHTMLX Tag Library, a JSP Tag library created on top of the DHTMLX component framework, allows to create DHTMLX forms with a very similar syntax to Spring JSP form tags.
DHTMLX Tags Form
  1. <%@ taglib prefix="dhtmlx" uri="http://www.mylaensys.com/dhtmlx" %>
  2. <dhtmlx:form name='book'>
  3.  <dhtmlx:formInput name='author' label='Author:'/>
  4.  <dhtmlx:formInput name='title' label='Title:'/>
  5.  <dhtmlx:formInput name='price' label='Price:'/>
  6.  <dhtmlx:formInput name='sales' label='Sales:'/>
  7.  <dhtmlx:formButton name='button_save' value='Save'/>
  8. </dhtmlx:form>
The code example in this series uses the DHTMLX Tag Library version 1.6, a recent release which include some of the new features of DHTMLX 3.0. Of course it also works without the tags using the Javascript DHTMLX API.

Client Side Validation
The dhtmlxForm let you specify client validation rules at field level using the validate  property. The DHTMLX Tag Library supports the validate property as attribute of the tag. Below the source code of the of the example form:
  1. <%@ taglib prefix="dhtmlx" uri="http://www.mylaensys.com/dhtmlx" %>
  2. <dhtmlx:form name='form'>
  3.   <dhtmlx:formHidden name='id'/>
  4.   <dhtmlx:formLabel name="message" label="" labelWidth="470"/>
  5.   <dhtmlx:formInput name='author' validate="NotEmpty"/>
  6.   <dhtmlx:formLabel name="authorError" label=""/>
  7.   <dhtmlx:formInput name='title' validate="NotEmpty"/>
  8.   <dhtmlx:formLabel name="titleError" label=""/>
  9.   <dhtmlx:formInput name='price' validate="ValidInteger"/>
  10.   <dhtmlx:formInput name='sales' validate="ValidInteger"/>
  11.   <dhtmlx:formLabel name="message" label="">
  12.   <dhtmlx:formLabel name="dummy" label="" labelWidth="120"/>
  13.     <dhtmlx:formNewColumn/>
  14.     <dhtmlx:formButton name='button_upd' value='Update'/>
  15.     <dhtmlx:formNewColumn/>
  16.     <dhtmlx:formButton name='button_close' value='Close'/>
  17.   </dhtmlx:formLabel>
  18. </dhtmlx:form>
In order to minimize the client to server data exchange, it is a 'user friendly' practice to perform client side validation before sending data to the server. I used the expression 'user friendly' because the client side validation can be easily bypassed.


Server Side Validation 
Staring from release 3, Spring has introduced several enhancements to the validation framework, including Bean Validation API (JSR-303). It is possible to declare validation constraints using annotations. This example has been written to run on the Google App Engine (using JDO). In the Book class below, in addition to the validation, you will find the persistence annotations.
Book.java
  1. public class Book {
  2.   @PrimaryKey
  3.   @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  4.   private Long id;
  5.   @Persistent
  6.   @NotEmpty
  7.   @Size(min = 5, max = 20)
  8.   private String author;
  9.   @Persistent
  10.   @NotEmpty
  11.   @Size(min = 5, max = 20)
  12.   @BookConstraint( message = "title already exists")
  13.   private String title;
  14.   @Persistent
  15.   private Integer sales;
  16.   @Persistent
  17.   private Integer price;
  18. }
A custom constraint annotation BookConstraint has been added to the title attribute, in order to evaluate if the book already exists in the datastore. You can find additional information about writing custom constraint in Spring documentation.
BookConstraint.java
  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target({ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE })
  4. @Constraint(validatedBy = BookValidator.class)
  5. public @interface BookConstraint {
  6.     String message() default "{some.error.code}";
  7.     Class<?>[] groups() default {};
  8.     Class<? extends Payload>[] payload() default {};
  9. }
BookValidatior.java
  1. public class BookValidator implements ConstraintValidator<BookConstraint,String> {
  2.   @Autowired
  3.   private BookDao bookDao;
  4.   @Override
  5.   public void initialize(BookConstraint bookConstraint) {
  6.   }
  7.   @Override
  8.   public boolean isValid(String title, ConstraintValidatorContext context) {
  9.     return bookDao.findByTitle( title ).size() < 2;
  10.   }
  11. }
After validation the BookController receives an instance of the Book class initialized with the form data and a BindingResult object containing the errors detected.
BookController.java
  1. @Controller
  2. public class BookController {
  3.     @Autowired
  4.     private BookService bookService;
  5.     @RequestMapping(value = "/book", method = RequestMethod.POST)
  6.     public @ResponseBody DefaultFormAdapter
  7.   storeBook(@Valid @ModelAttribute Book book, BindingResult binding) {
  8.         DefaultFormAdapter adapter = new DefaultFormAdapter(book,binding);
  9.         if( adapter.hasValidData() ) {
  10.             bookService.store(book);
  11.         }
  12.         return adapter;
  13.     }
  14. }
The dhtmlxForm posts the data performing an Ajax request. To return the errors triggered by the validation, the DHTMLX Spring Adapter creates an XML message. Below a XML response  example including one error message:
  1. <data>
  2.   <action
  3.    sid="42"
  4.    type="invalid"
  5.    field="title"
  6.    message="error !">
  7.   </action>
  8. </data>
On client side, the Javascript function getErrors parses the response message and sets the text on the error labels.
  1. function getErrors(form,response) {
  2.   var errorList = new Array();
  3.   $('action', response).each(function(i) {
  4.         field = $(this).attr("field");
  5.         if( field != undefined ) {
  6.             message = $(this).attr("message");
  7.             form.setValidateCss(field, false);
  8.             form.setItemLabel(field + "Error" , message );
  9.             errorList.push( field );
  10.         }
  11.   });
  12.   return errorList;
  13. }
  14. form.attachEvent("onButtonClick", function(id) {
  15.   if (id == "button_upd") {
  16.     clearMessages();
  17.     form.send('/app/book', function(loader, response) {
  18.           errorList = getErrors( form , response );
  19.     });
  20.   }
  21. });


For those who like sequence diagrams see below.




In the next episode, the design of the DHTMLX Spring adapter will be presented in detail. For those who can't wait the application, running on the Google App Engine, is available  here.

Stay Tuned!

No comments:

Post a Comment