Sunday, June 12, 2011

List Edit Form in Wicket 1.5


I had some spare time to have further fun with wicket 1.5 and I came up with this mini-howto to create a multiple-item edit form, this is very simple to do in some frameworks but I think in wicket is easier! So let's get started.

The final result will look like the following image:
It's a simple table that has an add Item button to add an extra row to enter contact information, and the submit button which is supposed to do something interesting with the data.

First of all, here is the HTML file, pretty standard stuff:

 <!DOCTYPE html>  
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>List Edit Form</h1>
<form wicket:id="multiform">
<table>
<thead>
<tr>
<th>Name</th>
<th>Last</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr wicket:id="forms">
<td><input type="text" wicket:id="name" /></td>
<td><input type="text" wicket:id="lastName"/></td>
<td><input type="text" wicket:id="email"/></td>
</tr>
</tbody>
</table>
<button type="submit" wicket:id="addItemButton">Add Item</button>
<button type="submit" wicket:id="doSomethingUseful">Submit</button>
</form>
</body>
</html>

As we can see, we have the form fields on the table, bound with wicket ids, and obviously the row has an id to match a wicket iterator component and of course the form has an id too.

We'll be editing contacts so we create a contact bean to hold the important information for the contacts, here I show only the attributes but imagine that this bean should have getters and setters for the attributes and it's better if you implement equals and hash code.

   
public class Contact implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String lastName;
private String email;
....
}

Now let's take a look at the code for the page:

 package com.juancavallotti.wicket15.forms;  

import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.SubmitLink;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.PropertyModel;

/**
*
* @author juancavallotti
*/
public class MultiFormPage extends WebPage {

private static final long serialVersionUID = 1L;
private List<Contact> contacts;

public MultiFormPage() {

contacts = new ArrayList<Contact>();
final Form multiform = new Form("multiform");
multiform.setOutputMarkupId(true);

//the list of items
multiform.add(new ListView("forms", new PropertyModel(this, "contacts")) {

private static final long serialVersionUID = 1L;

@Override
protected void populateItem(ListItem item) {

item.add(new TextField("name", new PropertyModel(item.getModelObject(), "name")));

item.add(new TextField("lastName", new PropertyModel(item.getModelObject(), "lastName")));

item.add(new TextField("email", new PropertyModel(item.getModelObject(), "email")));
}
});

//actions for the add item button
multiform.add(new AjaxSubmitLink("addItemButton") {

private static final long serialVersionUID = 1L;

@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
contacts.add(new Contact());
target.add(multiform);
}

@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
target.add(multiform);
}
});

//actions for the submit button
multiform.add(new SubmitLink("doSomethingUseful") {

private static final long serialVersionUID = 1L;

@Override
public void onSubmit() {
System.out.println(contacts);
}
});

add(multiform);
}
}


Here we create the form, and add to it a list view, in the list view we populate the item with text fields and a property model that will perform the actual magic when the form is submitted.

Then we add the button to create new items, which is responsible for adding a new contact on the contact list and repainting the whole form on the client.

And finally we add the button to submit the form to hopefully someday do something interesting with the bunch of contacts the users will input.

Reflecting the data changes Immediately

An enhancement for this would be to reflect data changes as soon as the user makes them. This only requires a small code change. We add for each text field an ajax submit behavior for the event "blur" and that's it, the following snippet shows how to do it.

       @Override  
protected void populateItem(ListItem item) {

item.add(new TextField("name", new PropertyModel(item.getModelObject(), "name"))
.add(new BlurSubmitBehavior(multiform)));

item.add(new TextField("lastName", new PropertyModel(item.getModelObject(), "lastName"))
.add(new BlurSubmitBehavior(multiform)));

item.add(new TextField("email", new PropertyModel(item.getModelObject(), "email"))
.add(new BlurSubmitBehavior(multiform)));
}

   class BlurSubmitBehavior extends AjaxFormSubmitBehavior {  
private static final long serialVersionUID = 1L;

public BlurSubmitBehavior(Form form) {
super(form, "onblur");
}

@Override
protected void onSubmit(AjaxRequestTarget target) {
//we do nothing but we could do something interesting
System.out.println(contacts);
}

@Override
protected void onError(AjaxRequestTarget target) {
}
};


That's all for now, hope you find it useful.

2 comments:

  1. It's also needed listView.setReuseItems(true) in case Form validation.

    ReplyDelete
  2. Thank you for good example.

    ReplyDelete