Hi! I have now better formalized my intentions (couldn't get sleep because of this ;). You can see the benefits for yourself. However, I found out that most of it can be done with existing wicket. Maybe some part of the philosophy could be adapted into wicket in general. I submitted a quickstart with two feature requests as https://issues.apache.org/jira/browse/WICKET-2267
In my opinnion, this results in very clean code. I hope you like it too ;) /** * Homepage */ public class HomePage extends WebPage { private static final long serialVersionUID = 1L; private final BookForm form; /** * */ @SuppressWarnings("serial") public HomePage() { add(new Label("message", "If you see this message wicket is properly configured and running")); add(form = new BookForm("form")); } /** * @return the form */ public BookForm getForm() { return form; } } class BookForm extends Form<Void> { private final BookListView bookListView; private final Button submitButton; public BookForm(String id) { super(id); List<Book> books = Arrays.asList(new Book("first"), new Book("second"), new Book("third")); add(bookListView = new BookListView("book_list", books)); add(submitButton = new Button("submit")); } /** * @return the bookListView */ public BookListView getBookListView() { return bookListView; } /** * @return the submitButton */ public Button getSubmitButton() { return submitButton; } } class Book implements Serializable { /** Field name (reflection property expression) */ public static final String NAME = "name"; /** Field name (reflection property expression) */ public static final String AUTHOR = "author"; /** Field name (reflection property expression) */ public static final String TYPE = "type"; private String name; private String author; private Type type; /** @param name */ public Book(String name) { this.name = name; } public enum Type { OLD, NEW; } } class BookListView extends TestableListView<BookListItem, Book> { public BookListView(String id, List<Book> list) { super(id, list); } @Override protected BookListItem newItem(int index) { return new BookListItem(index, getListItemModel(getModel(), index)); } } class BookListItem extends ListItem<Book> { private final TextField<String> nameField; private final TextField<String> authorField; private final DropDownChoice<Type> typeChoice; public BookListItem(int index, IModel<Book> model) { super(index, model); Book book = model.getObject(); add(nameField = new TextField<String>("name", new PropertyModel<String>(book, Book.NAME))); add(authorField = new TextField<String>("author", new PropertyModel<String>(book, Book.AUTHOR))); add(typeChoice = new DropDownChoice<Type>("type", new PropertyModel<Type>(book, Book.TYPE), Arrays.asList(Type.values()))); } /** * @return the nameField */ public TextField<String> getNameField() { return nameField; } /** * @return the authorField */ public TextField<String> getAuthorField() { return authorField; } /** * @return the typeChoice */ public DropDownChoice<Type> getTypeChoice() { return typeChoice; } } public abstract class TestableListView<ListItemType extends ListItem<ItemType>, ItemType> extends ListView<ItemType> { public TestableListView(String id, IModel<List<ItemType>> model) { super(id, model); } public TestableListView(String id, List<ItemType> list) { super(id, list); } public TestableListView(String id) { super(id); } @Override protected final void populateItem(ListItem<ItemType> item) { // do nothing here } @Override protected abstract ListItemType newItem(int index); @Override public Iterator<ListItemType> iterator() { return (Iterator<ListItemType>) super.iterator(); } } <html> <head> <title>Wicket Quickstart Archetype Homepage</title> </head> <body> <strong>Wicket Quickstart Archetype Homepage</strong> <br/><br/> <span wicket:id="message">message will be here</span> <form wicket:id="form"> <table border="1"> <tr wicket:id="book_list"> <th>Name:</th><td><input type="text" wicket:id="name"/></td> <th>Author:</th><td><input type="text" wicket:id="author"/></td> <th>Type:</th><td><select wicket:id="type"></select></td> </tr> </table> <input type="submit" wicket:id="submit" value="Submit"/> </form> </body> </html> And the resulting test code is very clean, no hassle with the (almost random ;) listview indexes after submit: /** * */ public void testBookList() { // start and render the test page tester.startPage(HomePage.class); // assert rendered page class tester.assertRenderedPage(HomePage.class); // assert rendered label component tester.assertLabel("message", "If you see this message wicket is properly configured and running"); // assert rendered page class HomePage homePage = (HomePage) tester.getLastRenderedPage(); BookForm bookForm = homePage.getForm(); BookListView bookListView = bookForm.getBookListView(); List<BookListItem> bookItems = toLinkedList(bookListView.iterator()); { // Test values of 1st book BookListItem bookListItem = bookItems.get(0); // Note: no hassle with run-time component path id's assertEquals("first", bookListItem.getNameField().getValue()); assertEquals("", bookListItem.getAuthorField().getValue()); assertEquals("-1", bookListItem.getTypeChoice().getValue()); } { // Test values of 2nd book BookListItem bookListItem = bookItems.get(1); // Note: no hassle with run-time component path id's assertEquals("second", bookListItem.getNameField().getValue()); assertEquals("", bookListItem.getAuthorField().getValue()); assertEquals("-1", bookListItem.getTypeChoice().getValue()); } { // Test values of 3rd book BookListItem bookListItem = bookItems.get(2); // Note: no hassle with run-time component path id's assertEquals("third", bookListItem.getNameField().getValue()); assertEquals("", bookListItem.getAuthorField().getValue()); assertEquals("-1", bookListItem.getTypeChoice().getValue()); } { // Set new values FormTester formTester = tester.newFormTester(bookForm.getPageRelativePath()); // formTester.setValue(bookItems.get(1).getAuthorField(), "Hemingway"); // TODO New feature request tester.getServletRequest().setParameter(bookItems.get(1).getAuthorField().getInputName(), "Hemingway"); // Submit changes // formTester.submit(bookForm.getSubmitButton()); // TODO New feature request formTester.submit(); } tester.assertRenderedPage(HomePage.class); // Verify the submitted value { // Test values of 2nd book BookListItem bookListItem = bookItems.get(1); // Note: no hassle with run-time component path id's assertEquals("second", bookListItem.getNameField().getValue()); assertEquals("Hemingway", bookListItem.getAuthorField().getValue()); assertEquals("-1", bookListItem.getTypeChoice().getValue()); } } ** Martin --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@wicket.apache.org For additional commands, e-mail: users-h...@wicket.apache.org