The aim of this post is the same as for the previous one - to cover some component specific questions in RichFaces which frequently asked at RichFaces User Forum. Some points (or maybe all of them) could be already known for you... But do not forget that everybody sometimes starts something from scratch :) Thus, my intention is to help the guys who just start RichFaces usage with some kickstart samples like this.

Here I will create simple search/results form. Let's imagine the case where every search result object contains a lot of similar information and should be entirely shown. Hence, we will use the tabbed results representation.

From the beggining we create a simple project with RichFaces support. I've used JSF 1.2 with Facelets 1.1.14 and RichFaces 3.3.1 and deployed under Tomcat 6. The process of simple project creation described fine at RichFaces Developer Guide. Section 3.

If you've already visited our richfaces-demo you've probably seen simple Capital objects list in lot's of samples(suggestionBox, table sorting sample and so on...). Thus, let's re-use the existed Java code for that case. Just download the classes and resources and place into some sources package in the created project. Then register the CapitalsBean at faces-config:

 <managed-bean>
  <managed-bean-name>capitalsBean</managed-bean-name>
  <managed-bean-class>org.richfaces.demo.capitals.CapitalsBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
 </managed-bean>

Look through the bean code. We have capitals list which contains all the capitals. We need to add one more list which will store the capitals that satisfies search conditions. Let's name it foundCapitals. And also let's add the property which will store the search conditions. In this case I've used simple search by capital name using startsWith method. Thus, next code should be added:

private ArrayList<Capital> foundCapitals = new ArrayList<Capital>();
private String searchValue = "";

// Do not forget to add getters and setters

Now it's time to work on page source code for this case. Next piece represent a search panel.

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:a4j="http://richfaces.org/a4j"
	xmlns:rich="http://richfaces.org/rich"
	xmlns:c="http://java.sun.com/jstl/core">
	<h:form>
		<rich:panel header="Simple Search">
			<h:outputText value="Enter the first letters of the capitals:" />

			<h:inputText id="searchField" value="#{capitalsBean.searchValue}"/>

			<rich:hotKey selector="##{rich:clientId('searchField')}" key="return" 
                            handler="#{rich:element('search')}.onclick();return false;"/>

			<a4j:commandButton action="#{capitalsBean.search}" value="Search" 
			    reRender="output" id="search"/>
		</rich:panel>
	</h:form>
</ui:composition>

There, I want to pay your attetion to the rich:hotKey component. It implements a common requirement of sending ajax request when a user presses Enter in inputs. #{rich:clientId} function returns clientId of the search input to which the hot key will be attached. And after Enter key is pressed in the input search button will be clicked via JS defined in handler attribute (#{rich:element} returns button DOM element by its Id).

Now let's define the tabPanel that should be created dynamically using foundCapitals list.

Add the next code just after the panel code:

<a4j:outputPanel id="output">
	<rich:tabPanel id="tapPanel" width="700" rendered="#{not empty capitalsBean.foundCapitals}">
		<c:forEach items="#{capitalsBean.foundCapitals}" var="cap">
			<rich:tab name="#{cap.name}" >
				<f:facet name="label">
				<h:panelGrid columns="2">
					<h:outputText value="#{cap.name}" />
					<h:graphicImage value="/images/icons/delete.gif" 
					    style="width:12px; height:12px;" 
					    onclick="myFunc('#{cap.name}'); Event.stop(event);"/>
				</h:panelGrid>
				</f:facet>
				<h:panelGrid columns="2" style="margin:20">
					<h:outputText value="State Name" />
					<h:inputText value="#{cap.state}" />
					<h:outputText value="State Capital" />
					<h:inputText value="#{cap.name}" />
					<h:outputText value="State TimeZone" />
					<h:inputText value="#{cap.timeZone}" />
				</h:panelGrid>
			</rich:tab>
		</c:forEach>
	</rich:tabPanel>
	<h:outputText value="No active search results." 
		style="font-style:italic" rendered="#{empty capitalsBean.foundCapitals}"/>
</a4j:outputPanel>
<a4j:jsFunction action="#{capitalsBean.remove}" name="myFunc"
	ajaxSingle="true" reRender="output">
	<a4j:actionparam name="current"/>
</a4j:jsFunction>

The following point needs to be highlighted: We will use c:forEach in order to create the tabs dynamically. It is a common place where RichFaces newcomers made a mistake. You could not use repeat components(neither ui:repeat nor a4j:repeat) for that because they work during page render time and do not create components in JSF tree but just iterate the same instance. We should use c:forEach tag instead which works at view build phase and causes actual tabs components to be added to JSF tree. For more details refer to c:forEach vs ui:repeat in Facelets article.

Inside c:forEach we define the tab which content is depend on iteration variable properties. Every tab will contain info about every Capital object which found using search.

Label facet contains text label for the tab and closure control which calls jsFunction with the current tab name as a parameter. Thus, a4j:jsFunction will rise ajax request after a removal control is clicked and Capital name for that tab will be put to request parameters by actionparam.

After tab removal and after the search we should update the tab panel and render the tabs if objects found or some label if search gave no results. As you could see we are using rendered attribute on the tabPanel and outputText component to define what should be rendered depending on whether some objects where found or not. And there is one more place to highlight one of the RichFaces limitations. We should not reRender conditionally rendered elements (outputText and tabPanel) directly. Ajax mechanism of RichFaces does not allow to insert any content. We should update some existent DOM nodes. a4j:outputPanel should be added around conditionally rendered elements and we will update this outputPanel.

And the last step is to implement search and remove methods in our CapitalsBean:

public void search() {
	foundCapitals.clear();
	for (Capital currentCapital : capitals) 
		if (currentCapital.getName().startsWith(searchValue)) 
			foundCapitals.add(currentCapital);
}

public Capital getCapitalIdByName(String name) {
	for (Capital currentCapital : capitals) {
		if (currentCapital.getName().equals(name))
			return currentCapital;
	}
	return null;
}

public void remove() throws Exception {
	Capital currentCapital = getCapitalIdByName(FacesContext
			.getCurrentInstance().getExternalContext()
			.getRequestParameterMap().get("current"));
	if (null != currentCapital)
		foundCapitals.remove(currentCapital);
	else
		throw new Exception("capital name parameter is null");
}

Now let's deploy the application and start the server to see the results.

No results found

TabPanel with results

I hope that some details which you should keep in mind during RichFaces usage that already present in our documentation and highlighted again at this post will help the developers to spend less time to build similar functionality.


Back to top