Help

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.

23 comments:
 
17. Jun 2009, 16:27 CET | Link

Really very nice! Helped me a lot!,-) Thanks, Danke, Merci, Gracias, Domo Arigato

ReplyQuote
 
17. Jun 2009, 20:37 CET | Link
Really nice article..In my application i am also doing similar thing.
But some difference is there , I have a arraylist which contains the rich:tab number that will be generated dynamically, to do that i am using <c:forEach items="#{menutree.tabitems}" var="item" varStatus="loop">
And inside each tab i am including different UI pages by <ui:include src="#{item.url}" />
each tabb page contains has different manage bean. The problem i am facing is that every thing working very fine,
but when i am adding a new tab page or closing a tab page, all the tab is getting reRender under the tabpanel <rich:tabPanel id="pageTabs"> as i am using the tabpanel id in my reReder value, this is casuing the problem, i just want to reRender only the tab item that i have added not the exsiting tab pages that are already opened. Could you please help me , how i can solve this problem..
thanks in advance
gantait

mail:debdutta_soft@yahoo.com
 
25. Jun 2009, 00:05 CET | Link
Philip

Any chance you could post complete project on line?

 
01. Jul 2009, 10:33 CET | Link
Eduardo
Hi!

Very nice work, but i've trying to do this and cant make it work:

<rich:tabPanel switchType="client">
        <c:forEach items="#{listaIngrediente.listaPizzas}" var="pizza">
                <rich:tab label="#{pizza.idPizza}">
                        <c:forEach items="#{pizza.listaIngredientes}" var="ing">
                                <h:outputText value="#{ing.nombreIngrediente}"></h:outputText>
                        </c:forEach>
                </rich:tab>
        </c:forEach>
</rich:tabPanel>

I've been swapping de # simbols for $ and even so it doesnt work, do you know what may be?.

Thanks!
 
03. Jul 2009, 19:00 CET | Link

A very nice example ! thanks

 
09. Jul 2009, 22:57 CET | Link

Guys.. sorry.. had emergency day-offs last time. In general to avoid this in the future - post the concrete questions at forum please.. All the team and other guys from community will help you there and in the blog only me monitoring the comments ;)

 
19. Jul 2009, 21:25 CET | Link
Philip Murphy | philipjmurphy(AT)gmail.com

Ilya, I know that this is not the best place to post this as this is a tutorial, however, I realize that some people have been having trouble with this. I just thought that it might be worth posting a work-around that I needed to use as it might help some people move forward with this.

If I use the old JSTL 1.0 tag library I can create a set of dynamic tabs using c:forEach, but if I use the newer JSTL 1.1 version it does not work.

For example:

<a4j:commandButton id="search" value="Search"
action="#{myBean.searchEvents}" reRender="myTabs" />

<rich:tabPanel id="myTabs" switchType="ajax">
  <c:forEach items="#{myBean.getEvents()}" var="_event">
    <rich:tab label="#{_event.name}" >
    </rich:tab>
  </c:forEach>
</rich:tabPanel

The above works fine if I include the following JSTL tag library (JSTL 1.0 - J2EE 1.3/JSP1.2) - this is the same one that is in used in the original example:-

xmlns:c="http://java.sun.com/jstl/core"

However, if I include the following JSTL tag library it does not work (JSTL 1.1 - J2EE 1.4/JSP 2.0):-

xmlns:c="http://java.sun.com/jsp/jstl/core"

I am using the #{} deferred EL syntax, however, it looks that when I am using the newer version that it is trying to evaluate the myBean.getEvents() before the events are actually populated by the submission of the Search button.

I am concerned that I am introducing a dependency on a old version of the JSTL library. I am using JBoss 5.1.0 with Seam 2.2. However, I can't get this to work with the newer JSTL tag library. I've posted a question about this in one of the discussion forums, and I'll post back if I ever figure this one out.

 
28. Sep 2009, 18:28 CET | Link
Monica

Too bad no answer on this:

when i am adding a new tab page or closing a tab page, all the tab is getting reRender under the tabpanel as i am using the tabpanel id in my reReder value, this is casuing the problem, i just want to reRender only the tab item that i have added not the exsiting tab pages that are already opened. Could you please help me , how i can solve this problem..

Is it possible to render only newly created tabs ?

What happens when a user is in one tab, modifies data, and creates a new tab (from a menu). What will happen with the modified data ?

Thanks in advance Monica

 
02. Oct 2009, 16:01 CET | Link
Ilya

For the first point - unfortunatelly I must admit that current implementation do not allo0w us to update just the tabcontrols row. And in general you can't reRender just added tab because it's nothing to update after it's added (it wasn't present in DOM before). Now we are taking this into consideration while planning component movement to 4.0

And about What happens if the modified tab deleted. This situation could be handled by just your code. The same button that implements deletion could be added with something like confirmation.

 
13. Oct 2009, 09:15 CET | Link
Hi,
This is useful, can you send to me your code? You can send by my email.
Thanh very much!
 
22. Oct 2009, 02:29 CET | Link
if the client switch tab mode, can you show me how to disable/enable tabtb4e?
13. Jan 2010, 18:13 CET | Link
Markos Fragkakis
Can the same be the case with rich:dataTable columns? I am trying to dymanically set the rowspan of my columns, but have not managed to.. Perhaps this is because I define < rich:Column> inside an < a4j:repeat>. I have described my problem in more detail at StackOverflow [1].

Cheers!

[1] http://stackoverflow.com/questions/2028958/richdatatable-rowspan-issue
 
01. Apr 2010, 16:54 CET | Link

I want to use the intialisation of tabs in encodeBegin method, can we do that??i tried putting the code of cretaeTabs methos in encodeBegin method but it doesnt work.can we initialise the richfaces tabs in the start up? We already have a tabed system using a pure html one but want to use richfaces tabs in this method.

31. May 2010, 06:56 CET | Link

hi to all, can i set the default tab name when my page is loading for the first time.

i am using selectedTab attribute in my tabpanel, binding the tab names with my backing bean. but i am getting error like error reading tab name in server console.

can anyone help me please?

thanks in advance

 
04. Aug 2010, 18:40 CET | Link

Can you send me a war file to yingxie.xie@yahoo.com? Thanks in advance!

 
19. Sep 2010, 20:49 CET | Link
Hi,
first of all, thanks for this article, it was very helpful.

I have a question regarding the example: If you have page parameters defined for the page, and you click on a dynamically created tab to open it, are the page params preserved (in URL) in this case? According to my experiments this is not the case, and I'm trying to figure the reason to this.


I have

xhtml:
         <rich:tabPanel switchType="server" selectedTab="#{foo.selectedTab}" immediate="true" ajaxSingle="true" bypassUpdates="true">
                <c:forEach items="#{foo.tabs}" var="tab">
                    <rich:tab name="#{tab.id}" switchType="server" action="#{foo.activateTab(tab.id)}">

pages.xml:
        <param name="selectedTab" value="#{foo.selectedTab}"/>

        <navigation from-action="#{foo.activateTab(tabId)}">
            <redirect />
        </navigation>

When I click on a tab, I'm expecting to see selectedTab preserved in the URL, but this is not the case.


Regards,
Jukka
 
27. May 2011, 21:20 CET | Link
Edward Kizhner | ekizhner(AT)yahoo.com
Can you send me a war file to ekizhner@yahoo.com? I tried to make this work on my own with Apache Tomcat 6.0.26.
I get the original search panel ok but once I click Search or press Enter, I get
"May 27, 2011 11:27:18 AM org.apache.catalina.core.ApplicationContext log
INFO: j_id0:tapPanel: tab panel has no enabled or rendered tabs!" error

Thanks in advance!
 
01. Dec 2011, 04:04 CET | Link
yogesh

nice work , can you please share you full code for this , it will be very helpful for me , Thanks in Advance

 
07. Jun 2012, 05:53 CET | Link
Matthew Casperson

It is worth pointing out that you need a fixed order when removing tabs with the code shown above, so don't use a Set (as you might find in a hibernate application).

26. Sep 2013, 11:02 CET | Link

Please can you post all code or send me an email? nolvia80@yahoo.it

 
06. Oct 2013, 14:31 CET | Link
Nico

Hi all. Very good explanation, just one question: since the c:forEach tag is evaluated during view creation, the portion of the view pointed by the reRender attribute is reconstructed from scratch on every ajax request?

Thanks a lot, Nico

 
23. Sep 2014, 06:31 CET | Link
jassica

The fun and excitement of volleyball lures many children and adults into playing the sport. However, in order to have as much fun as possible, volleyball safety is something that you must always keep. rugby union

 
24. Sep 2014, 06:24 CET | Link

With Rolex Replica Watches with your wrist, girls themselves will approach you. This wrist quality replica watches watch that you are wearing is itself a fashion statement and yes it tells these women a whole lot about your taste.In case you so want, you will get more than a dozen ones but tend not to feel the pinch. Many people are reputed to own regarding green dozen models of these omega Replica Watches with him or her.Should you too are intending to replace each of the watches of your house but you are worrying of finding Gucci replica the same model in several locations in your house, dispel these thoughts. These Rolex Replica Watches can be found in lots of different colors and models.

Women prefer men who lead their lives rough and tough and it's also amazing to know that people who wear Rolex Replica Watches on their wrist do appear rough and hard.You'll never be capable to exhaust the complete these Rolex Replica Watches despite the fact that keep one more out fake watches of all rooms of one's condominium. Visiting a notorious locality? Why have a risk by the original. Rolex Replica Watches will let you keep your gravity on the meeting possibly at once just remember to is not going to feel the pinch for anyone who is mugged whilst your Rolex Replica Watches are robbed.

There are many executives which will not are satisfied with other things compared to the best. They adore to wear one exclusive watch for work and another for your party. Browse the lots of Rolex Replica Watches. They just don't cost the planet earth and quite a few people may easily afford multiple.You might have invested a princely sum from the clothes you wear also to woo the girl you will ever have, you could have also top Replica handbags hire done on the classiest cars but what about the wrist watch? Good and reputed watches are certainly not accessible for hire and it's at such moments that this Replica IWC watches help you along of an tight spot.

Post Comment