Thursday, January 29, 2009

Wicket and Default Focus Behavior

This seems to come up a lot: "Is there a behavior (or some other way) for having a field receive the focus when the page loads? For instance, in a login form, you'd want the focus to go to the username field". I've solved this a couple of different ways before, but when I found James Carman's tip I realized I already had a convenient place to automatically enable this behavior for every form in my Wicket app.

I'm already using Alastair Maw's technique for attaching error messages to form fields. This visits all form components and attaches an error behavior to each one. This would be another behavior that would just be attached to the first editable form field. Turns out it works really well. I decided I only wanted to set focus on a text field or a dropdown, but you can decide for yourself.

public class ShinyFormVisitor implements IVisitor, Serializable
{
private static final long serialVersionUID = 9060018214520265174L;
private final Set<FormComponent> visited = new HashSet<FormComponent>();
private boolean found = false;

public Object component(Component c)
{
if (!visited.contains(c) && c instanceof FormComponent && !(c instanceof Button))
{
final FormComponent fc = (FormComponent) c;
visited.add(fc);
fc.add(new ValidationMsgBehavior());
fc.add(new ErrorHighlightBehavior());
if (!found && fc.isEnabled() && fc.isVisible() && (fc instanceof DropDownChoice || fc instanceof AbstractTextComponent))
{
found = true;
fc.add(new DefaultFocusBehavior());
}
}
return IVisitor.CONTINUE_TRAVERSAL;
}
}


In the above, a DefaultFocusBehavior is attached to the first form field of the right type. Now for DefaultFocusBehavior itself:


/**
* @see http://www.nabble.com/Default-Focus-Behavior--td15934889.html
*/
public class DefaultFocusBehavior extends AbstractBehavior
{
private static final long serialVersionUID = -4891399118136854774L;

private Component component;

@Override
public void bind(Component component)
{
if (!(component instanceof FormComponent))
{
throw new IllegalArgumentException("DefaultFocusBehavior: component must be instanceof FormComponent");
}
this.component = component;
component.setOutputMarkupId(true);
}

@Override
public void renderHead(IHeaderResponse iHeaderResponse)
{
super.renderHead(iHeaderResponse);
iHeaderResponse.renderOnLoadJavascript("document.getElementById('"
+ component.getMarkupId() + "').focus();");
}
}

1 comment:

James Selvakumar said...

Hi thanks for sharing this info.
It was very useful.