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.
Really very nice! Helped me a lot!,-) Thanks, Danke, Merci, Gracias, Domo Arigato
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
Any chance you could post complete project on line?
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!
A very nice example ! thanks
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 ;)
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:tabPanelThe 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:-
However, if I include the following JSTL tag library it does not work (JSTL 1.1 - J2EE 1.4/JSP 2.0):-
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.
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
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 . This situation could be handled by just your code. The same button that implements deletion could be added with something like confirmation.
This is useful, can you send to me your code? You can send by my email.
Thanh very much!
Cheers!
[1] http://stackoverflow.com/questions/2028958/richdatatable-rowspan-issue
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.
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
Can you send me a war file to yingxie.xie@yahoo.com? Thanks in advance!
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
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!
nice work , can you please share you full code for this , it will be very helpful for me , Thanks in Advance
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).