Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

The idea is to develop a reusable mulit step panel with defined user exits to react on the panel flow.

Customer Search Form Example:

The search form is very simple and consist of a text field and a submit button. After the user has submitted the form a result table is
shown under the form. The user can select a customer from the result list.

Delegate Pattern:

With the delegate pattern you have the possiblity to react on defined events of the panel from outside. In the above described example
you have two delegate methods.

  • void searchSumitted(SearchPanel panel);
    This method is invoked after the form was successfully submitted. Here
    you have the possiblity to read the data from the form, execute some backend methods
    (for example reading some customers from the database) and set a list of customers
    which will be shown in the result table.
  • customerSelected(SearchPanel panel, Customer customer);
    This method is invoked after a customer was selected from the result table.
    The the method tells you which customer was selected.

The form handles all the stuff regarding the search fields. For example the panel check whether all
mandatory fields are filled. If everything is fine the delegate method void searchSumitted(SearchPanel panel) is invoked.
Inside this method you have access to the submitted form values and you can provide the result list.
After that the result table is shown. When the user selects a row of the result table the second delegate method customerSelected(SearchPanel panel, Customer customer) is executed.
Inside this method you get the selected customer value object.

Source structure

Code Block
xml
xml
src
 |-com.sample
 	|-searchpanel
 	|	|-Customer.java // Customer value object
	|	|-CustomerDataProvider.java // custom implmentation of data provider
	|	|-SearchPanel.java // reusable search panel
	|	|-SearchPanelDelegate.java // delegate interface
	|	|-SearchPanel.html // template for the search panel
	|-HomePage.java // Sampel page which uses the search panel
	|-WicketApplication.java // wicket app
	|-HomePage.html // template of the sample page

All the sources inside the searchpanel package are reusable and can for example packed into separate jar file.

Test Application

The following three sources files are the sample application which uses the search panel.
Using the SearchPanel is very easy.

1. The Wicket page has to implement the interface SearchPanelDelegate
2. The Wicket page HTML needs a div with a wicket id. (see <div wicket:id="search">)
3. Create a SearchPanel with the id any the object which implements the SearchPanelDelegate interace
4. Add the panel object to the Wicket page.
5. Implement your custom functionality inside the delegate methods

HomePage.java

Code Block
package com.sample;

import java.util.ArrayList;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebPage;
import com.sample.seachpanel.Customer;
import com.sample.seachpanel.SearchPanel;
import com.sample.seachpanel.SearchPanelDelegate;

/**
 * Homepage
 */
public class HomePage extends WebPage implements SearchPanelDelegate {

	private static final long serialVersionUID = 1L;

    /**
	 * Constructor that is invoked when page is invoked without a session.
	 *
	 * @param parameters
	 *            Page parameters
	 */
    public HomePage(final PageParameters parameters) {
        // create the search panel and add it to the Page
        SearchPanel panel = new SearchPanel("search", this);
        add(panel);
    }

    /**
     * This delegate method is invoked when a customer was selected from the SearchPanel
     */
	public void customerSelected(SearchPanel panel, Customer customer) {
		System.out.println("A customer was selected");
		System.out.println(customer.toString());

	}

	/**
	 * This delegate method is invoked when the SearchPanel form was sumitted
	 */
	public void searchSumitted(SearchPanel panel) {

		System.out.println("The search was sumitted");
		System.out.println(panel.getSearchString());

		// implement here some backend access
		ArrayList<Customer> list = new ArrayList<Customer>();
		list.add(new Customer("1000", "Tom"));
		list.add(new Customer("2000", "Sue"));
		list.add(new Customer("3000", "Paul"));
		list.add(new Customer("4000", "Linda"));

		// set the data list to the SearchPanel
		panel.getResultlist().setList(list);
	}
}

HomePage.html

Code Block
html
html
<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" >
    <head>
        <title>Wicket Panel with using delegate pattern</title>
    </head>
    <body>
        <strong>Wicket Panel with using delegate pattern</strong>
        <div wicket:id="search">Here is the search panel and result list displayed</div>
    </body>
</html>

WicketApplication.java

Code Block
package com.sample;

import org.apache.wicket.protocol.http.WebApplication;

/*\*
* Application object for your web application. If you want to run this application without deploying, run the Start class.
* &nbsp;
* @see com.sample.Start#main(String\[\])
\*/
public class WicketApplication extends WebApplication
{
/*\*
* Constructor
\*/
public WicketApplication()
{
}

/*\*
* @see org.apache.wicket.Application#getHomePage()
\*/
public Class<HomePage> getHomePage(){
return HomePage.class; 	}

}

Reusable panel source code

Here is the source of the reusable panel.

Customer

Code Block
package com.sample.seachpanel;

import java.io.Serializable;

/**
 * This class is a value object for customers
 * 
 * @author manuel.fritsch
 */
public class Customer implements Serializable {

	private String id;
	private String name;

	/**
	 * Default empty constructor
	 */
	public Customer() {
	}

	/**
	 * This constructor can used to set directly the customer id and name
	 * 
	 * @param id
	 * @param name
	 */
	public Customer(String id, String name) {
		super();
		this.id = id;
		this.name = name;
	}

	/**
	 * @return the id
	 */
	public String getId() {
		return id;
	}

	/**
	 * @param id
	 *            the id to set
	 */
	public void setId(String id) {
		this.id = id;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Customer Id = " + id + " Customer Name = " + name;
	}

}

CustomerDataProvider

Code Block
package com.sample.seachpanel;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;

import org.apache.wicket.markup.repeater.data.IDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

/**
 * This a customer data proivder
 * The advantage of this data provider is that you can update the data list
 * @author manuel.fritsch
 *
 * @param <T>
 */
public class CustomerDataProvider<T extends Serializable> implements IDataProvider<T>
{
	private static final long serialVersionUID = 1L;

	/** reference to the list used as dataprovider for the dataview */
	private List<T> list;

	/**
	 * 
	 * @param list
	 *            the list used as dataprovider for the dataview
	 */
	public CustomerDataProvider(List<T> list)
	{
		if (list == null)
		{
			throw new IllegalArgumentException("argument [list] cannot be null");
		}

		this.list = list;
	}

	/**
	 * @see IDataProvider#iterator(int, int)
	 */
	public Iterator<? extends T> iterator(final int first, final int count)
	{
		int toIndex = first + count;
		if (toIndex > list.size())
		{
			toIndex = list.size();
		}
		return list.subList(first, toIndex).listIterator();
	}

	/**
	 * @see IDataProvider#size()
	 */
	public int size()
	{
		return list.size();
	}

	/**
	 * @see IDataProvider#model(Object)
	 */
	public IModel<T> model(T object)
	{
		return new Model<T>(object);
	}

	/**
	 * @see org.apache.wicket.model.IDetachable#detach()
	 */
	public void detach()
	{
	}

	/**
	 * 
	 * @return
	 */
	public List<T> getList() {
		return list;
	}
	/**
	 * 
	 * @param list
	 */
	public void setList(List<T> list) {
		this.list = list;
	}
}

SearchPanel

Code Block
package com.sample.seachpanel;

import java.util.ArrayList;

import org.apache.wicket.feedback.ContainerFeedbackMessageFilter;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.model.PropertyModel;

public class SearchPanel extends Panel {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private SearchPanelDelegate delegate;
	private String searchString;
	private WebMarkupContainer resulttable;
	private CustomerDataProvider resultlist;
	private DataView dataView;
	
	public SearchPanel(String id, final SearchPanelDelegate delegate) {
		super(id);
		
		
		this.delegate = delegate;
		
		// create form and add form to the search panel
		Form<?> form = new SearchForm("form");
		add(form);
		
		// create markup container around the result table
		// this is necessary to hide the result list before the form submit
		this.resulttable = new WebMarkupContainer("resulttable");
		this.resulttable.setVisible(false);
		add(resulttable);
		
		// init the hitlist with an empty dataprovider
		ArrayList<Customer> emptylist = new ArrayList<Customer>();
		resultlist = new CustomerDataProvider(emptylist);
		
		// create data view component
		this.dataView = new DataView<CustomerDataProvider>("resultlist", resultlist) {

			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			/**
			 * The 
			 */
			@Override
			protected void populateItem(Item item) {
				
				// get customer object for row
				final Customer customer = (Customer) item.getModelObject();
				
				// create a link for column 1
				SearchPanelLink link = new SearchPanelLink("select");
				// the customer object to the link. 
				//This customer object will be returned when the row is selected
				link.setCustomer(customer);
			
				// Add link and labels to the data view
				item.add(link);
				item.add(new Label("name", customer.getName()));
				item.add(new Label("id", customer.getId()));
			}
			
		};
		
		// add data view to search panel
		this.resulttable.add(dataView);
		
		// Filter the feedback messages 
		// Only messages which belongs to the current form will be displayed
		FeedbackPanel feedback = new FeedbackPanel("feedback");
		feedback.setFilter(new ContainerFeedbackMessageFilter(form));
		add(feedback);
	}
	
	
	/**
	 * @return the searchString
	 */
	public String getSearchString() {
		return searchString;
	}

	/**
	 * @param searchString the searchString to set
	 */
	public void setSearchString(String searchString) {
		this.searchString = searchString;
	}

	/**
	 * @return the resultlist
	 */
	public CustomerDataProvider getResultlist() {
		return resultlist;
	}

	/**
	 * @param resultlist the resultlist to set
	 */
	public void setResultlist(CustomerDataProvider resultlist) {
		this.resultlist = resultlist;
	}

	/**
	 * The inner search form class
	 * @author manuel.fritsch
	 */
	public final class SearchForm extends Form {

		// The form seach field
		private TextField<String> searchfield; 
		
		public SearchForm(String id) {
			super(id);
			
			// init seachfield
			searchfield = new TextField<String>("seachfield", new PropertyModel<String>(SearchPanel.this, "searchString"));
			// set field required
			searchfield.setRequired(true);
			// add searchfield to search form
			add(searchfield);			
		}

		/**
		 * This method is executed the the searach butten was pressed
		 */
		@Override
		protected void onSubmit() {
			// invoke delegate method
			// the search is submitted and the delegate object will be informed
			delegate.searchSumitted(SearchPanel.this);
			// make the result table visible
			resulttable.setVisible(true);
			super.onSubmit();
		}	
	}

	/**
	 * Link implementation for the table rows
	 * @author manuel.fritsch
	 *
	 */
	public class SearchPanelLink extends Link<String> {

		private Customer customer;

		public SearchPanelLink(String id) {
			super(id);
		}
		
		/**
		 * This method will be executed when the select link 
		 * used in the result page
		 */
		@Override
		public void onClick() {

			// invoke delegate method
			// a customer was the selected an the delegate object will be informed
			if(delegate != null) {
				delegate.customerSelected(SearchPanel.this, customer);
			}
		}

		/**
		 * @return the customer
		 */
		public Customer getCustomer() {
			return customer;
		}

		/**
		 * @param customer the customer to set
		 */
		public void setCustomer(Customer customer) {
			this.customer = customer;
		}
	}
}

SearchPanelDelegate

Code Block
package com.sample.seachpanel;

/**
 * This is the delegation interface It describes the all possible delegate
 * methods
 * 
 * @author manuel.fritsch
 * 
 */
public interface SearchPanelDelegate {
	// This delegate method is invoked when the from was successfully submitted
	void searchSumitted(SearchPanel panel);

	// This delegate method is invoked when the customer was selected from the
	// result list
	void customerSelected(SearchPanel panel, Customer customer);
}

SearchPanel.html

Code Block
HTML
HTML
<html
	xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd">
<head>
<title>Wicket panel with using a delegate pattern</title>

</head>
<body>
<wicket:panel>
<div id="message" wicket:id="feedback">[Feedback Panel]</div>
<form wicket:id="form">
	<input type="text" wicket:id="seachfield" value="" />
	<input type="submit" value="Search" />
</form>

<div wicket:id="resulttable">
<table>
   	<thead>
   		<th>Action</th>
   		<th>Customer Id</th>
   		<th>Customer Name</th>
   	</thead>
   	<tbody>
       <tr wicket:id="resultlist">
       	 <td><a wicket:id="select" href="#">Select</a></td>
       	 <td><span wicket:id="id">Test Name</span></td>
         <td><span wicket:id="name">Test Name</span></td>
       </tr>
    </tbody>
</table>
</div>

</wicket:panel>
</body>
</html>

Tipps

It is possible to have several search panels on one Wicket page. To identify which panel has been
called the delegate method use the following snippet:

Code Block
public void customerSelected(SearchPanel panel, Customer customer) {

		if("search".equals(panel.getId())) {
			// Panel one
		}

		if("search".equals(panel.getId())) {
			// Panel tow
		}
		System.out.println("A customer was selected");
		System.out.println(customer.toString());
}