Karthik Guru's Weblog

All | General | Java
20060324 Friday March 24, 2006

Wicket and Ajax - I

This is my first blog and hopefully you will find the information below useful. I like  Wicket web framework a lot and I personally believe that its the coolest java web framework around. In this blog entry, we will take a first look at Ajax support built into Wicket . We will begin with basic Wicket form validation to put things into perspective. I hope to provide more information on wicket's Ajax support in subsequent blogs.

Basic Wicket Form validation

Consider a simple HTML form with fields that carry wicket:id attributes. We will apply certain validation rules to the 'name' and 'age' elements shortly. These rules will kick in on click of the submit button.

<html>
<head>
   <title>Hello World</title>
</head>
<body>
<span wicket:id="feedback"/>
<form wicket:id="form">
   Name <input type="text" wicket:id="name"/>
  Age  <input type="text" wicket:id="age"/>
  <input type="submit" value="Submit"/>
</form>
</body>
</html>

Listing 1.1 HelloAjax.html

Wicket has a FeedbackPanel that displays the error messages captured during form validation. Actually, the FeedbackPanel will display all types of feedback message attached to the components contained within the Page. You have the ability to filter only those message types (info,error, debug,warn etc) that you want but that's probably a topic of another blog entry. The place holder for the panel is typically specified through the span tag with wicket:id attribute. In this case it is the span element with wicket attribute 'feedback'.
This is how it renders on the browser :



If you are already not aware, in Wicket, a HTML template needs to be backed by a corresponding wicket Page class implementation. Accordingly, here is our Page class : It is of the same name as the template and by convention, the template should be placed under the same folder structure that mimicks the package structure of the corresponding Page class. Ultimately the HTML template and the Page class file should end up under the same folder structure. (There are ways for overriding it. But its irrelevant here).

public class HelloAjax extends WebPage {
 private String name;
 private int age;
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public HelloAjax() {
   Form form = new Form("form") {
     public void onSubmit() {
       info(" name is " + HelloAjax.this.getName() + " and age is "
      + HelloAjax.this.getAge());
     }
  };
  // Register the backing Model with the components.
  TextField txtName = new TextField("name", new PropertyModel(this,
    "name"));
  TextField txtAge = new TextField("age", new PropertyModel(this, "age"));
  form.add(txtName);
  form.add(txtAge);
  add(new FeedbackPanel("feedback").setOutputMarkupId(true));
  add(form);
  txtName.setRequired(true);
  //For now assume that values in the range 10 50 are valid
  txtAge.add(IntegerValidator.range(10, 50));
  txtAge.setRequired(true);
 }
}

Listing 1.2 HelloAjax.java

Store the validation messages in a properties file as below. The feedback panel will use this to display the validation messages on the browser. The validation messages can be localized by placing them in appropriately named properties file.

form.name=Field Name
form.age=Field Age
RequiredValidator='${label}' is required
IntegerValidator='${label}' needs to be in the range ${min} , ${max}

Listing 1.3 HelloAjax.properties

Leave the 'name' field blank and enter an invalid value for the 'age' field and click on Submit. You should see something like this on the browser :



That's basic serverside wicket validation at work. Next we will add inline style to identify fields that have passed/failed the validation check.

Adding Inline style for display purposes

<html>
<head>
   <title>Hello World</title>
   <style type="text/css">
     .valid{background-color:green;}
     .invalid{background-color:red;}
    </style>
</head>
<body>
<span wicket:id="feedback"/>
<form wicket:id="form">
   Name <input type="text" wicket:id="name"/>
  Age  <input type="text" wicket:id="age"/>
  <input type="submit" value="Submit"/>
</form>
</body>
</html>

Listing 1.4 HelloAjax.html with inline CSS

On the wicket side of things, we will add the appropriate style attribute to the component HTML tag after validation. Wicket has a concept of behaviors that is represented by wicket.behavior.IBehavior interface. Components can exhibit different behaviors and they can be associated with the component at runtime (by simpling calling "component.add(IBehaviour)". In addition to other things,  a behavior also gets an opportunity to modify the component tag attributes through IBehavior.onComponentTag(). wicket.behavior.AbstractBehavior is a noop implementation of IBehavior which will be of help to us in the case. We are just interested in the onComponentTag() callback ,  something that we will override.

//Check if the component is valid and add the corresponding CSS style attribute
class ValidationStyleBehavior extends AbstractBehavior {

  public void onComponentTag(final Component component,
    final ComponentTag tag) {
   FormComponent comp = (FormComponent) component;
   if (comp.isValid() && comp.getConvertedInput() != null) {
    tag.getAttributes().put("class", "valid");
   } else if (!comp.isValid()) {
    tag.getAttributes().put("class", "invalid");
   }
  }
 };

Now that we are done defining this behavior, the only thing that remains is attaching it to the components.

public class HelloAjax{
  public HelloAjax(){
    //..
    //..
    txtName.add( new ValidationStyleBehavior());
    txtAge.add(new ValidationStyleBehavior());
  }
}

Now when you submit the form , you should see something like this on the browser.



Enabling Ajax validation in Wicket

Now lets try to ajaxify this validation behaviour. It should be good enough, if we are able to provide feedback after the user has entered a value for the HTML form element and has moved the focus to another field. It essentially boils down to providing feedback on the 'onblur' javascript event of the element. On the 'onblur' event , we need to fire an ajax request passing in the user input , validate the input and construct the response on the server,  pass it back to the browser and finally get it to re render the component.

One could term the ability to respond to a Ajax request as a behavior exhibited by the component and as you would have guessed, wicket models it as one.

Wicket ships with quite a few commonly required Ajax behavior implementations and AjaxFormComponentUpdatingBehavior is one of them. You are welcome to checkout others in the
Wicket API Docs
AjaxFormComponentUpdatingBehavior when added to a component,
   addresses the following client side related things :
      - generating the javascript call that needs to execute on the 'onblur' event.<BR>   attaching the same to the 
HTML element etc, attaching the same to the HTML element etc.
   on the wicket side (server) of things,
      - it updates and validates the component against the input that was passed in through the Ajax call
      -  and then it gives you a chance to decide the next steps by calling the 'onUpdate' passing in the   AjaxRequestTarget object.

public class HelloAjax{
  public HelloAjax(){
    txtName.add(new AjaxFormComponentUpdatingBehavior("onblur"){
       @Override
       protected void onUpdate(AjaxRequestTarget target) {
         target.addComponent(getFormComponent());
       }    
    });

  
 txtAge.add(new AjaxFormComponentUpdatingBehavior("onblur"){
      @Override
      protected void onUpdate(AjaxRequestTarget target) {
         target.addComponent(getFormComponent());
      }   
   });
  //..
  //..
 }
 //..
}

Getting to know AjaxRequestTarget in utmost detail could prove to be a litte tedious at this point. But for now, it is sufficient to know that the component added to the AjaxRequestTarget  becomes the target of ajax response. AjaxRequestTarget will render only those components (partial rendering) and the builtin javascript infrastructure will make sure that it rerenders the component. Note that you can add as many components as you like, to AjaxRequestTarget and wicket will render them in response. Cool!  no?

Ok leave the name field blank and press tab and that s'd result in something like this being displayed on the browser.

Click on the Ajax Debug Link and that should pop up a window that tells you the actual ajax response that was received from the server. This is extremely useful when working in debug mode. If an exception occurs during partial render, the debug window would display that.



It could come as a surprise that the only thing we ever did was to add this line

'target.addComponent(getFormComponent());'

in response to an Ajax request and somehow the element had the correct CSS style associated with it during Ajax re render. Remember the ValidationStyleBehavior that we added earlier that attaches the style class to the element?
Well, as it turns out, wicket does almost the same thing when rendering the whole page on the browser (complete render) and when it does a partial render. The behaviors attached to a component execute/render during normal requests and there is no reason why they s'dn't when responding to a Ajax call.

Keeping the FeedbackPanel and Ajax valdiation in Sync



You would have noticed that, when responding to Ajax request, we forgot to rerender the FeedbackPanel. We need to keep the Ajax behavior of form components and the FeedbackPanel in sync. Ofcourse you can do something like this ..

txtName.add(new AjaxFormComponentUpdatingBehavior("onblur"){
 @Override
 protected void onUpdate(AjaxRequestTarget target) {
  target.addComponent(getFormComponent());
  target.addComponent(FeedbackPanel);
 }   
});

Feedback message are cleanedup upon a new request (Ajax request in this case). So adding the FeedbackPanel as an Ajax target as above w'dn't work as the panel w'dn't have an insight into the other component's input at the point in time.
No worries, wicket ships with AjaxFormValidatingBehavior class that makes working with such things a breeze. It takes care of submitting the form in its entirity as an Ajax request, validates the form and renders the components and any FeedbackPanel in the page in reponse. The following code snippet is all that we need to add:

AjaxFormValidatingBehavior.addToAllFormComponents(form,"onblur");

Wrapping it up..

import wicket.Component;
import wicket.ajax.AjaxRequestTarget;
import wicket.ajax.form.AjaxFormValidatingBehavior;
import wicket.behavior.AbstractBehavior;
import wicket.markup.ComponentTag;
import wicket.markup.html.WebPage;
import wicket.markup.html.form.Form;
import wicket.markup.html.form.FormComponent;
import wicket.markup.html.form.TextField;
import wicket.markup.html.form.validation.IntegerValidator;
import wicket.markup.html.panel.FeedbackPanel;
import wicket.model.PropertyModel;
 
public class HelloAjax extends WebPage {
 private String name;
 private int age;
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }

 public HelloAjax() {
  Form form = new Form("form", new CompoundPropertyModel(this)){
     public void onSubmit() {
          info(" name is " + HelloAjax.this.getName() + " and age is " + HelloAjax.this.getAge());
     }
   }
  }  
  TextField txtName = new TextField("name");
  TextField txtAge = new TextField("age");
  form.add(txtName);
  form.add(txtAge);
  add(new FeedbackPanel("feedback").setOutputMarkupId(true));
  add(form);
  //Age and name are required fields
  txtName.setRequired(true);
  txtAge.setRequired(true);
  //Only value in the range 10 to 50 makes sense
  txtAge.add(IntegerValidator.range(10,50));
  txtName.add(new ValidationStyleBehavior ()) ;
  txtAge.add(new ValidationStyleBehavior ());
  AjaxFormValidatingBehavior.addToAllFormComponents(form,"onblur");
}

class ValidationStyleBehavior extends AbstractBehavior {

   //Check if the component is valid and add the corresponding CSS style attribute
  public void onComponentTag(final Component component,
      final ComponentTag tag) {

   //Wicket has a Component hierarchy similar to Swing and TextField "is a" FormComponent.
   //We 'downcast' in this case as we need to access certain methods defined at the FormComponent level.

      FormComponent comp = (FormComponent) component;
      if (comp.isValid() && comp.getConvertedInput() != null) {
         tag.getAttributes().put("class", "valid");
      } else if (!comp.isValid()) {
         tag.getAttributes().put("class", "invalid");
      }
  }
}
}

Listing 1.5 Ajax enabled validation

That's it for now.


Posted by karthikg Mar 24 2006, 12:02:43 PM CST Permalink Comments [8]

Comments:

Nice! Is there any way you could do a little nicer formatting (like having less white space and putting boxes around the code samples)?

Hope to see more posts!

Posted by Eelco Hillenius on March 24, 2006 at 04:15 PM CST #

Very nice! Glad to see it errored out when you told it I was only 5 years old :)

Reading this I see an area where wicket can be improved.

It would be nice to have a AjaxThrottledEventBehavior that you can attach to onkeydown event and throttle it to one request per second max. This would allow a more or less instanteneous feedback w/out having to wait for the blur event because by then you moved away from the keyboard anyways.

Maybe I can get to that this weekend, we will see...

Once again, great write up!

Posted by Igor Vaynberg on March 24, 2006 at 09:10 PM CST #

Don't you think it's a bit overkill?
Some much code for only 2 input fields?

Posted by francisoud on July 13, 2006 at 04:54 AM CDT #

Hi,
this is santosh thank you so much for the awesome program i was using tree program which was coded very good
but when i tried to implement it.
so i am looking imported files which are used in the tree program
can you plz suggest/guide me on this issue.

Posted by santosh on February 24, 2008 at 06:22 AM CST #

hey karthik actually i should fire an ajax event on a textfield when a user fails to enter a valid info .do we have any such functions in wicket ajax that does this 4 me.

Posted by m.rakesh on May 25, 2008 at 11:12 PM CDT #

fantastic

Posted by Sudhanshu on September 26, 2008 at 03:42 AM CDT #

Hello, Is there any way you could formatting the feedback panel?

Thanks

Posted by Facundo on August 27, 2009 at 01:44 PM CDT #

Just an update. I was using something similar and possibly from some update the onUpdate method is only called if the form is valid, if the field is invalid you have to override onError method and add that formcomponent to target here as well to get the effect.

Posted by Marek Šabo on January 21, 2010 at 04:06 PM CST #

Post a Comment:

Name:
E-Mail:
URL:

Your Comment:

HTML Syntax: NOT allowed