12 Displaying multiple items with repeaters - Reference Documentation
Authors: Andrea Del Bene, Carsten Hufe, Christian Kroemer, Daniel Bartl
Version: 1.0.0.BUILD-SNAPSHOT
Table of Contents
12 Displaying multiple items with repeaters
A common task for web applications is to display a set of items. The most typical scenario where we need such kind of visualization is when we have to display some kind of search result. With the old template-based technologies (like JSP) we used to accomplish this task using classic for or while loops:<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <% for(int i = 12; i<=32; i++) { %> <div>Hello! I'm index n°<%= %></div> <% } %> </body>
12.1 Component RepeatingView
Component org.apache.wicket.markup.repeater.RepeatingView is a container which renders its children components using the tag it is bound to. It can contain an arbitrary number of children elements and we can obtain a new valid id for a new child calling its method newChildId(). This component is particularly suited when we have to repeat a simple markup fragment, for example when we want to display some items as a HTML list:HTML:<ul>
<li wicket:id="listItems"></li>
</ul>
RepeatingView listItems = new RepeatingView("listItems");listItems.add(new Label(listItems.newChildId(), "green"); listItems.add(new Label(listItems.newChildId(), "blue"); listItems.add(new Label(listItems.newChildId(), "red");
<ul> <li>green</li> <li>blue</li> <li>red</li> </ul>
12.2 Component ListView
As its name suggests, component org.apache.wicket.markup.html.list.ListView is designed to display a given list of objects which can be provided as a standard Java List or as a model containing the concrete List. ListView iterates over the list and creates a child component of class org.apache.wicket.markup.html.list.ListItem for every encountered item.Unlike RepeatingView this component is intended to be used with complex markup fragments containing nested components.To generate its children, ListView calls its abstract method populateItem(ListItem<T> item) for each item in the list, so we must provide an implementation of this method to tell the component how to create its children components. In the following example we use a ListView to display a list of Person objects:HTML:… <body> <div id="bd" style="display: table;"> <div wicket:id="persons" style="display: table-row;"> <div style="display: table-cell;"><b>Full name: </b></div> <div wicket:id="fullName" style="display: table-cell;"></div> </div> </div> </body> ...
public HomePage(final PageParameters parameters) { List<Person> persons = Arrays.asList(new Person("John", "Smith"), new Person("Dan", "Wong")); add(new ListView<Person>("persons", persons) { @Override protected void populateItem(ListItem<Person> item) { item.add(new Label("fullName", new PropertyModel(item.getModel(), "fullName"))); } }); }
ListView and Form
By default ListView replaces its children components with new instances every time is rendered. Unfortunately this behavior is a problem if ListView is inside a form and it contains form components. The problem is caused by the fact that children components are replaced by new ones before form is rendered, hence they can't keep their input value if validation fails and, furthermore, their feedback messages can not be displayed.To avoid this kind of problem we can force ListView to reuse its children components using its method setReuseItems and passing true as parameter. If for any reason we need to refresh children components after we have invoked setReuseItems(true), we can use MarkupContainer's method removeAll() to force ListView to rebuild them.12.3 Component RefreshingView
Component org.apache.wicket.markup.repeater.RefreshingView is a subclass of RepeatingView that comes with a customizable rendering strategy for its children components.RefreshingView defines abstract methods populateItem(Item) and getItemModels(). The first method is similar to the namesake method seen for ListView, but it takes in input an instance of class org.apache.wicket.markup.repeater.Item which is a subclass of ListItem. RefreshingView is designed to display a collection of models containing the actual items. An iterator over these models is returned by the other abstract method getItemModels.The following code is a version of the previous example that uses RefreshingView in place of ListView:HTML:… <body> <div id="bd" style="display: table;"> <div wicket:id="persons" style="display: table-row;"> <div style="display: table-cell;"><b>Full name: </b></div> <div wicket:id="fullName" style="display: table-cell;"></div> </div> </div> </body> ...
public HomePage(final PageParameters parameters) { //define the list of models to use final List<IModel<Person>> persons = new ArrayList<IModel<Person>>(); persons.add(Model.of(new Person("John", "Smith")); persons.add(Model.of(new Person("Dan", "Wong")); add(new RefreshingView<Person>("persons") { @Override protected void populateItem(Item<Person> item) { item.add(new Label("fullName", new PropertyModel(item.getModel(), "fullName"))); }@Override protected Iterator<IModel<Person>> getItemModels() { return persons.iterator(); } }); }
Item reuse strategy
By default, just like ListView, RefreshingView replaces its children with new instances every time is rendered. The strategy that decides if and how children components must be refreshed is returned by method getItemReuseStrategy. This strategy is an implementation of interface IItemReuseStrategy. The default implementation used by RefreshingView is class DefaultItemReuseStrategy but Wicket provides also strategy ReuseIfModelsEqualStrategy which reuses an item if its model has been returned by the iterator obtained with method getItemModels.To set a custom strategy we must use method setItemReuseStrategy.12.4 Pageable repeaters
Wicket offers a number of components that should be used when we have to display a big number of items (for example the results of a select SQL query).All these components implements interface org.apache.wicket.markup.html.navigation. paging.IPageable and use interface IDataProvider (placed in package org.apache.wicket. markup.repeater.data) as data source. This interface is designed to support data paging. We will see an example of data paging later in paragraph 11.4.2.The methods defined by IDataProvider are the following:- iterator(long first, long count): returns an iterator over a subset of the entire dataset. The subset starts from the item at position first and includes all the next count items (i.e. it's the closed interval first,first+count).
- size(): gets the size of the entire dataset.
- model(T object): this method is used to wrap an item returned by the iterator with a model. This can be necessary if, for example, we need to wrap items with a detachable model to prevent them from being serialized.
Component DataView
Class org.apache.wicket.markup.repeater.data.DataView is the simplest pageable repeater shipped with Wicket. DataView comes with abstract method populateItem(Item) that must be implemented to configure children components. In the following example we use a DataView to display a list of Person objects in a HTML table:HTML:<table> <tr> <th>Name</th><th>Surename</th><th>Address</th><th>Email</th> </tr> <tr wicket:id="rows"> <td wicket:id="dataRow"></td> </tr> </table>
//method loadPersons is defined elsewhere List<Person> persons = loadPersons(); ListDataProvider<Person> listDataProvider = new ListDataProvider<Person>(persons);DataView<Person> dataView = new DataView<Person>("row", listDataProvider) { @Override protected void populateItem(Item<Person> item) { Person person = item.getModelObject(); RepeatingView repeatingView = new RepeatingView("dataRow"); repeatingView.add(new Label(repeatingView.newChildId(), person.getName())); repeatingView.add(new Label(repeatingView.newChildId(), person.getSurename())); repeatingView.add(new Label(repeatingView.newChildId(), person.getAddress())); repeatingView.add(new Label(repeatingView.newChildId(), person.getEmail())); item.add(repeatingView); } }; add(dataView);
Data paging
To enable data paging on a pageable repeaters, we must first set the number of items to display per page with method setItemsPerPage(long items). Then, we must attach the repeater to panel PagingNavigator (placed in package org.apache.wicket.markup.html.navigation .paging) which is responsible for rendering a navigation bar containing the links illustrated in the following picture:Project PageDataViewExample mixes a DataView component with a PagingNavigator to display the list of all countries of the world sorted by alphabetical order1. Here is the initialization code of the project home page:HTML:<table> <tr> <th>ISO 3166-1</th><th>Name</th><th>Long name</th><th>Capital</th><th>Population</th> </tr> <tr wicket:id="rows"> <td wicket:id="dataRow"></td> </tr> </table>
public HomePage(final PageParameters parameters) { super(parameters); //method loadCountriesFromCsv is defined elsewhere in the class. //It reads countries data from a csv file and returns each row as an array of Strings. List<String[]> countries = loadCountriesFromCsv(); ListDataProvider<String[]> listDataProvider = new ListDataProvider<String[]>(countries); DataView<String[]> dataView = new DataView<String[]>("rows", listDataProvider) { @Override protected void populateItem(Item<String[]> item) { String[] countriesArr = item.getModelObject(); RepeatingView repeatingView = new RepeatingView("dataRow"); for (int i = 0; i < countriesArr.length; i++){ repeatingView.add(new Label(repeatingView.newChildId(), countriesArr[i])); } item.add(repeatingView); } }; dataView.setItemsPerPage(15); add(dataView); add(new PagingNavigator("pagingNavigator", dataView)); }
Wicket provides also component PageableListView which is a sublcass of ListView that implements interface IPageable, hence it can be considered a pageable repeaters even if it doesn't use interface IDataProvider as data source.