Home » Java, Wicket

User friendly form validation with Wicket

3 September 2008 35 Comments
This article is part of the 'Wicket tutorials' series.

By default Wicket shows error messages together in a single place in the HTML form. This has some drawbacks to usability, especially if you have long forms with lots of fields. Read further for a tutorial exploring possibilities to improve the location of the error on the page, thereby improving usability.

The default FeedbackPanel shows all errors in one place. When you enter a wrong value in an input field below the fold, the input field is a mile away from the error above the form. This makes it unclear which error message corresponds to which field.

form-usability-scanning.png

The image on the left shows that a lot of page scanning is needed even with moderate sized forms.

With more than a few fields, the user is confused as which error corresponds to which field. It is a big problem when your e-commerce site scares away many potential clients who can’t complete your web forms!

The default form

Before I describe how to create a more user friendly form, first the ‘default’ form. The following is an example of a standard Wicket form. This kind of form is the one you get without doing any ’special’ magic things. Place your mouse cursor over the image to see the default error messages above the form.

Default Wicket form with FeedbackPanel at the top

This form will be the starting point for our improvements.

The improved form

This is what we are going to make:

Better and improved Wicket form with FeedbackLabels throughout

As you can see, the error messages are directly next to the component that caused the error. This reduces the scanning of the page to link the error message to the right form component.

Step 1: Introducing the FeedbackLabel

The FeedbackLabel is a custom component I’ve written for this tutorial. With this custom label, you can show important feedback messages related to a FormComponent.

This has the advantage that you can place your Feedback messages in any place you want.

Add this to the Java part of the form:

// This shows feedback when the name input is not correct.
FeedbackLabel nameFeedbackLabel = new FeedbackLabel("name.feedback", name);
form.add(nameFeedbackLabel);
 
FeedbackLabel colorFeedbackLabel = new FeedbackLabel("color.feedback", color, customText);
form.add(colorFeedbackLabel);

Add another SPAN tag for every feedback label. You can place this near the relevant form component within the form.

Step 2: Removing the FeedbackPanel completely? No, filtering!

At first, it looks like you can remove the FeedbackPanel completely. But, then you will not be able to use the info() method to display text inside the FeedbackPanel! Therefore we need a way to filter the FeedbackMessages so error messages are not shown. I have written a FeedbackMessageFilter to accomplish that. It filters out the unwanted error messages.

// filteredErrorLevels will not be shown in the FeedbackPanel
int[] filteredErrorLevels = new int[]{FeedbackMessage.ERROR};
feedback.setFilter(new ErrorLevelsFeedbackMessageFilter(filteredErrorLevels));

I have included the ErrorLevelsFeedbackMessageFilter in the project files, so you can reuse this in your existing projects. Remember if you use the error() method, you cannot use this filter! Instead, create your own filter that filters based on components.

Step 3: Adding some style, the ComponentVisualErrorBehavior™

I always love extremely long class names, like the BookmarkablePageRequestTargetUrlCodingStrategy or the SharedResourceRequestTargetUrlCodingStrategy. It makes my day to introduce a class name consisting of four words, the ComponentVisualErrorBehavior™.

This behavior changes the CSS styles for components that are invalid according to the Wicket form validation. You see in the screenshot that the textfield has a red line around it. This is the result of the ComponentVisualErrorBehavior. You can easily change the styles that are applied.

form-usability-tutorial-namefield.png

To add the ComponentVisualErrorBehavior to your component, just add one line in your Java code:

name.add(new ComponentVisualErrorBehavior("onblur", nameFeedbackLabel));

“onblur” stands for the event that triggers this Behavior. The nameFeedbackLabel will also be updated when this is triggered, so that it will show the relevant error (or nothing if the input is valid).

Download

Download the complete example project and start experimenting! (run with mvn jetty:run and connect to http://localhost:8080/demo)

Let me know how and where you use it in the comments!

This article is a part of the 'Wicket tutorials' series

Here are all the articles in this series:
  1. User friendly form validation with Wicket
  2. Create RESTful URLs with Wicket
  3. Wicket: how to write a reusable modal window popup

35 Comments »

  • Wille said:

    Cool! Have you suggested getting the FeedbackLabel stuff into Wicket proper, or at least Wicket Extensions?
    I can definitely see it having a place there, I know Al Maw (who is one of the committers) held a presentation on this sort of thing during a London Wicket User Group meetup, although that was good, I really like your approach to it.

    I’d love to use this in Wicket RAD for error messages.

  • Daan said:

    Thanks for the compliment!
    My goal was to write unobtrusive components that do the heavy lifting by themselves.
    If you find it useful, feel free to include it in Wicket RAD or any other project.

  • Wille said:

    Since I am not aware of anything like this in core Wicket, if you are fine with it, I’ll put it into Wicket RAD (Apache License) and just repackage the components a little.

    Looked at the code (haven’t tried it yet though) – very simple, but also very, very useful. The way it should be. :)

  • Daan said:

    Including is fine by me :-) .

  • Wille said:

    Sorry for bugging you all the time – one question came up: how would you deal with Form level validators?
    For instance, “match password” type validation with two password fields?
    I believe these wouldn’t turn up for the individual FormComponents (not sure though).

    I haven’t tried it yet, but maybe adding something like this to the ErrorLevelsFeedbackMessageFilter class might work:
    public boolean accept(FeedbackMessage message){
    if(message.getReporter().equals(parentForm))
    return true;

  • Daan said:

    Form level validators cannot be linked to one FormComponent, so they wouldn’t show up in the FeedbackLabel. You need to change the ErrorLevelsFeedbackMessageFilter for them to show up, as you already said.

    About your example, “match password” type validation with two password fields. Isn’t it possible to link the validator to the second password field and when the second password field does not match the first, you get an error next to the second field.
    The first password field gets a ‘Required’ validator. That way, the first password field is required to be non-empty and the second password field is required to exactly match the first.

    By the way, there are other IFeedbackMessageFilter implementations:

  • Stefan F said:

    I’m quite tired right know, so please excuse me, when I am talking total bullshit now ;) However, have you thought about using a Border instead of FeedbackLabel? To make my idea clearer, here is what the HTML would look like:

    Form:
    [div wicket:id="feedbackBorder"]
    [label ...]Name:[/label]
    [input ... /]
    [/div]

    Border:

    [wicket:border]
    [span wicket:id="msg"][/span]
    [wicket:body /]
    [/wicket:border]

    produced HTML, no error: same as template
    produced HTML, error:
    [div wicket:id="feedbackBorder" class="error"]
    [span]this is an error message[/span]
    [label ...]Name:[/label]
    [input ... /]
    [/div]

    that way, you wouldn’t need an extra behaviour to style the erroneous input field. one could even give a light red background to the surrounding div (just an example).

    regards, stefan

  • Daan said:

    It didn’t come up with me to use a Border. But I guess Border has it’s own markup? That makes it a bit heavy-weight, because you need to create separate Border components (+markup) for each Form Component.
    I believe the FeedbackLabel is less intrusive, because you can just insert it in existing Forms without changing the hierarchy. The Form component itself is not aware of the FeedbackLabel, which is a Good Thing.

  • Stefan F said:

    Yes, Border has its own markup. But no, you wouldn’t need to create a separate Border for each FormComponent. And yes, you would have to change the hierarchy:

    old:
    add(new SimpleFormComponentLabel(…));
    add(new TextField(…));

    new:
    add(border = new FeedbackBorder(…));
    border.getBodyComponent(..).add(new SimpleFormComponentLabel(…));
    border.getBodyComponent(..).add(new TextField(…));

    Most people wouldn’t have to change their markup though, as most forms consist of either table rows or divs anyway. Hence:

    old:
    [div][label wicket:id=""][/label][input wicket:id=""/][/div]
    new:
    [div wicket:id="'][label wicket:id=""][/label][input wicket:id=""/][/div]

    You should definitely have a look at Borders. They really simplify/beautify a lot of things. However, it takes some time to fully understand the concept and it’s powers – it took me at least ;)

    I’ll go and implement that border myself as I’m in the lucky situation to start a new project from scratch today – lucky me ;) – where I have the ability to play around with that kind of stuff. If I succeed, I’ll post it on my blog (http://talk-on-tech.blogspot.com). So you better keep an eye on it ;D

    best regards

  • Daan said:

    I’m curious to see your Border solution.

  • Ricardo Mayerhofer said:

    I had trouble using the feedback label with CompoundPropertyModel in forms.
    The error was: WicketMessage: No get method defined for class.

    To fix just replace:

    this.setModel( nulll );
    by
    this.setModel( new Model( “” ) );

    on the method onBeforeRender in FeedbackLabel.

  • Steve said:

    Great work BUT I also had a problem with using it with CompoundPropertyModel in my form. The solution above didn’t fix it. It kept requiring a getter for the field in my model backing the form. In the end, I just added one to my model and ignore it.
    Can you think of a more elegant solution?

  • Steve said:

    I made a modification so that you can attach the Feedbaklabel to Form’s themselves, rather than just FormComponents. This is so you can collect messages that original from the Form as well as FormComponents. Messages like ‘Upload too large’ or ‘upload failed’ originate from the Form.

    The simple fix is to change FormComponent in Feedbacklabel.java to be Component, not FormComponent. I’ve pasted the full modification below:

    /*
    * Copyright 2008 Daan, StuQ.nl
    * Licensed under the Apache License, Version 2.0 (the “License”);
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an “AS IS” BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    *
    */
    package nl.stuq;

    import org.apache.wicket.AttributeModifier;
    import org.apache.wicket.Component;
    import org.apache.wicket.markup.html.basic.Label;
    import org.apache.wicket.markup.html.form.Form;
    import org.apache.wicket.markup.html.form.FormComponent;
    import org.apache.wicket.model.IModel;
    import org.apache.wicket.model.Model;

    /**
    * Label displaying feedback messages for FormComponents.
    *

    * You can use this Label to show the error message near the actual FormComponent, instead of in the FeedbackPanel
    * It’s safe to remove the FeedbackPanel if you use this class for every FormComponent in your Form.
    *

    * You can use this code under Apache 2.0 license, as long as you retain the copyright messages.
    *
    * Tested with Wicket 1.3.4
    * @author Daan, StuQ.nl
    * @author Steve Swinsburg, s.swinsburg@lancaster.ac.uk
    */
    public class FeedbackLabel extends Label {

    /** Field component holds a reference to the {@link Component} this FeedbackLabel belongs to
    * Generally this is a FormComponent like a TextField, but can also be the Form itself.
    * Using the form itself is useful for collecting errors that come from the Form and not from a FormComponent,
    * like upload too large etc. */
    private Component component = null;

    /** Field text holds a model of the text to be shown in the FeedbackLabel */
    private IModel text = null;

    /**
    * Call this constructor if you just want to display the FeedbackMessage of the component
    * @param id The non-null id of this component
    * @param component The {@link FormComponent} to show the FeedbackMessage for.
    */
    public FeedbackLabel(String id, Component component) {
    super(id);
    this.component = component;
    }

    /**
    * Call this constructor if you want to display a custom text
    * @param id The non-null id of this component
    * @param component The {@link FormComponent} to show the custom text for.
    * @param text The custom text to show when the FormComponent has a FeedbackMessage
    */
    public FeedbackLabel(String id, Component component, String text) {
    this(id, component, new Model(text));
    }

    /**
    * Call this constructor if you want to display a custom model (for easy i18n)
    * @param id The non-null id of this component
    * @param component The {@link FormComponent} to show the custom model for.
    * @param iModel The custom model to show when the {@link FormComponent} has a FeedbackMessage
    */
    public FeedbackLabel(String id, Component component, IModel iModel) {
    super(id);
    this.component = component;
    this.text=iModel;
    }

    /**
    * Set the content of this FeedbackLabel, depending on if the component has a FeedbackMessage.
    *
    * The HTML class attribute will be filled with the error level of the feedback message. That way, you can easily
    * style different messages differently. Examples:
    *
    * class = “feedbacklabel INFO”
    * class = “feedbacklabel ERROR”
    * class = “feedbacklabel DEBUG”
    * class = “feedbacklabel FATAL”
    *
    *
    * @see Component
    */
    @Override
    protected void onBeforeRender() {
    super.onBeforeRender();
    setModel(null);

    if(component.getFeedbackMessage()!=null){
    if(this.text!=null){
    this.setModel(text);
    } else {
    this.setModel(new Model(component.getFeedbackMessage().getMessage()));
    }

    this.add(new AttributeModifier(”class”, true, new Model(”feedbackLabel ” + component.getFeedbackMessage().getLevelAsString())));
    } else {
    this.setModel(null);
    }

    }
    }

    I would still like to know how to get it to work when using a CompoundPropertyModel backing a form. The current workaround (working for me) is to have a getter in the POJO for the property although I’d prefer not to do this.

    cheers.

  • Wicket RAD 0.6 released | StuQ.nl said:

    [...] release also includes my FeedbackLabel, a component feedback label for more userfriendly [...]

  • Revue de presse ! - Blog de ZedroS - Club d'entraide des développeurs francophones said:

    [...] les PageLink – Wicket : découpler les composants pour les mises à jour Ajax – Wicket : avoir les feedback à côté des saisies (un point sur lequel je voulais blogger un de ces jours… plus besoin et vive le web !) – [...]

  • zedros said:

    hi

    the compoundpropertymodel (CPM) issue is normal : when using CPM, each element of the form will be looked for in the CPM. Hence the “not get for FeedbackLabelID”.

    You just have to say explicitly that you don’t want the FeedbackLabel to share the CPM :
    add(new FeedbackLabel(”fbLabel”, name, new Model(null)));
    ==> the new Model(null) does matter ;)

    BTW, for wicket 1.4 and probably even for earlier version, you should also change the onBeforeRender as well like this :

    @Override
    protected void onBeforeRender()
    {
    super.onBeforeRender();

    if(component.getFeedbackMessage()!=null)
    {
    if(this.text!=null){
    setDefaultModel(this.text);
    } else {
    setDefaultModel(new Model(component.getFeedbackMessage().getMessage()));
    }

    this.add(new AttributeModifier(”class”, true, new Model(”feedbackLabel ” + component.getFeedbackMessage().getLevelAsString())));
    } else {
    setDefaultModel(new Model(”"));
    }
    }

  • zedros said:

    Me again !

    I’ve found a better way to deal with the fact that some messages have been already rendered.

    In the FeedbackLabel onBeforeRender, just do “component.getFeedbackMessage().markRendered();” after having taken the component message.

    When you declare your FeedbackPanel of the page, just add the following filter :
    FeedbackPanel panel = new FeedbackPanel(”feedbackPanel”);
    panel.setFilter(new IFeedbackMessageFilter(){

    public boolean accept(FeedbackMessage message)
    {
    return !message.isRendered();
    }

    });

    Thus messages already rendered won’t appear in the feedback panel. And consequently we’re now sure that all messages will be shown :)

    ++
    zedros

  • zedros said:

    Hi

    me again ! :$

    In fact, when trying your FeedbackLabel, I realised that I was looking for something closer to a feedback panel dedicated to the component. Indeed, if multiple messages are expected, I don’t want to have just one next to the component and the others in the main feedback panel.

    Thinking twice about it, I found a really easy solution, using just FeedbackPanel, I put it here in case it helps.

    In your form, first make final the component concerned :
    final RequiredTextField name = new RequiredTextField(”name”);
    add(name);

    Then, add the feedbackpanel overriding the accept() method to define precisely what you want :
    FeedbackPanel dedicatedFP = new FeedbackPanel(”fbLabel”);
    dedicatedFP.setFilter(new IFeedbackMessageFilter(){

    public boolean accept(FeedbackMessage message)
    {
    if (message.getReporter().equals(name) && !(message.isRendered())){
    message.markRendered();
    return true ;
    }else{
    return false ;
    }

    }});
    add(dedicatedFP);

    I explicitly accept only messages from my component. Furthermore, in my case, I don’t want to have them twice in my page, so I check for “isRendered()” first, then mark it as rendered when dealt with by my feedbackpanel.

    Then, to be sure I miss no message, I put a feedbackpanel for my whole form :
    FeedbackPanel panel = new FeedbackPanel(”feedbackPanel”);
    panel.setFilter(new IFeedbackMessageFilter(){

    public boolean accept(FeedbackMessage message)
    {
    return !message.isRendered();
    }

    });
    add(panel);

    Once again, I check for isRendered.

    Nice and easy isn’t it ?

    For sure, one can easily makes his own DedicatedFeedbackPanel(String id, Component component) if needed, and with exactly what is needed => we could decide to check for one precise level of feedbackmessage or… As well, the mono rendering, or not, is up to the end user.

    I hope it helps !

    Cheers
    zedros

  • Avoir des feedback propres said:

    [...] Dans ma dernière revue de presse, j’avais parlé d’un blog traitant d’un moyen d’avoir les feedbacks à côté des saisies. [...]

  • Srinivasa Raju CH said:

    Hi,
    I have used the code to add error message for a field. I am using wicket 1.4 I am getting the error message withe code that you gave. This is the error message….

    WicketMessage: Error attaching this container for rendering: [MarkupContainer [Component id = form]]Root cause:java.lang.IllegalStateException: Attempt to set model object on null model of component: form:name_feedback

    Still more error stack. Can you please tell me why I am getting this error.

  • Daan (author) said:

    Hi Srinivasa,

    I have no idea why it does not work with Wicket 1.4. We’re currently running on 1.3.5 and it should work on that.
    If you (or someone else) finds a solution, please share it!

    - Daan

  • Srinivasa Raju CH said:

    Hi Daan,
    I have testing your demo application with jetty. The component visual behavior is working fine with text fields, but the same is not working for radio button, I mean on selection of the radio button the error message beside the field is still showing. I even tried by changing the event (onblur) to onchange even it is not working for radio buttons. Can you please check once and let me know why so….

    – Srinivas.

  • Daan (author) said:

    Hi Srinivasa Raju CH,

    The radio buttons do not have the ComponentVisualErrorBehavior attached in the demo. I think it wouldn’t work in the same way for radio buttons as for form fields.

    It’s currently not very easy to trigger Ajax behaviors for radio buttons. See this post on the mailing list for a solution.

    – Daan

  • Ashis said:

    Hello all,
    I have used this approach of validation in wicket.It worked for textfields but did not work on AutoComplete textfield.

    Any suggestions will be gratefully appreciated

    thanks

  • Maarten said:

    You might find these components from elephas interesting as well:

    http://code.google.com/p/elephas/source/browse/trunk/src/main/java/org/elephas/webapp/frontend/component/common/form/

    According to the commit message, it’s based on Al Maw’s ShinyForm.

  • Daan (author) said:

    Hi Maarten,

    These look indeed quite similar :-)
    Thanks for the link!

    - Daan

  • jim said:

    Can anyone think of a way to use taborder instead of onBlur? onBlur does not work for radiobuttons etc. Or maybe a way to add a wicket behavior to focus on the next element when the previous element is validated?

  • Ala said:

    We are doing something similar, but instead of having extra code (panels to show the error) for each field we just have a visitor that goes through the form and then adds a border/changes style class etc. to every component that failed validation. We also have a variant that dynamicly inserts the “feedback label” using dom manipulation and ajax, e.g. adding new component markup and the component dynamicly.

  • Track your running miles using Apache Wicket, GlassFish, NetBeans, MySQL, and YUI Charts « Miles to go … said:

    [...] Form Validation & Default Error Handling: There are validators available to enable basic checking like required, number / pattern / range / date validator. Anything above that needs to be custom built and adds to the verbose nature of the framework. For example a "FormValidator" was built to support the validation for each elements of the form. Custom messages for each validation message can be displayed as explained here. More user-friendly messages can be displayed as explained here. [...]

  • Elad said:

    If you want to use this in 1.4 you will need to change the setModel method to setDefaultModelObject as stated in the migration guide to 1.4

  • Timo said:

    Aargh… This kind of workarounds/extensions/whatever of frameworks just make me more unsure if Java is not a right tool create web applications. IMHO, things like these should be easily available in the framework itself…

  • Antoine said:

    Maybe I missed something – but why go to the trouble of creating a new component instead of using a feedback filter which is readily available in the Wicket framework?

    Example:
    FormComponent nameField = new RequiredTextField(”name”);
    add(new FeedbackPanel(”nameFieldFeedback”, new ComponentFeedbackMessageFilter(nameField)));

    That feedback panel will only show messages relevant to the nameField component, exactly what your FeedbackLabel component is doing.

  • Björn said:

    Hi there,
    i tried using this FeedbackLabel with the ComponentVisualErrorBehavior.
    But there is one thing I don’t understand:
    while the field is invalid it shows me the correct Errormessage, but when I go back and correct the input to be valid, the error message doesn’t disappear. How do I make it delete it again? Since the onError(..) method is still called not even the style-class is set to “valid” Why could that be?
    I’d appreciate some help, thanx in advance
    Cheers Björn

  • Mark said:

    Nice article!!
    I have one question regarding validation with models which are direclty bound to hibernate entities. The problem i have if i use your component that wicket looks for getter/setter of the feedbacklabel which of course doesnt exists. Is there a elegant way to say wicket dont use the feedbacklabel for the model bound to the form? I am new to wicket so my question could be stupid :-) .
    Thanks for your help!

  • Mark said:

    Sry for my last post – i just red the other comments and this solution works! Thanks!

Leave your response!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.