JSF too many commandLinks (h:form) lead to ViewExpiredException - jsf

I am having a JSF application which creates and presents about 50 reports. These reports are rendered PNGs and under the pictures a table is displayed.
This table uses a RichFaces 4 togglePanel with switchType="client". The togglePanel is just for collapsing and expanding the table.
<h:form>
<rich:togglePanel id="#{param.reportWrapper}_togglePanel"
stateOrder="opened,closed" activeItem="opened" switchType="client">
<rich:togglePanelItem name="closed">
<h:panelGroup>
<div class="myclass">
<ul class="container-icons">
<li>
<h:commandLink styleClass="container-max" value="maximieren">
<rich:toggleControl targetPanel="#{param.reportWrapper}_togglePanel" targetItem="#next" />
</h:commandLink>
</li>
</ul>
<h3>My Heading</h3>
</div>
</h:panelGroup>
</rich:togglePanelItem>
<rich:togglePanelItem name="opened">
<h:panelGroup>
<div class="myclass">
<ul class="container-icons">
<li>
<h:commandLink styleClass="container-min" value="minimieren">
<rich:toggleControl targetPanel="#{param.reportWrapper}_togglePanel" targetItem="#prev" />
</h:commandLink>
</li>
</ul>
<h3>Another Heading</h3>
<div class="scrolling-table-content">
<rich:dataTable>
// ...
</rich:dataTable>
</div>
</div>
</h:panelGroup>
</rich:togglePanelItem>
</rich:togglePanel>
</h:form>
The problem is, that I sometimes get a ViewExpiredExceptions when the reports are loaded. My numberOfLogicalViews and numberOfViewsInSession is 14. I dont want to set it to 50, because of memory issues and because it should not be necessary as only one report is really shown at the same time.
I tried to remove the h:form tags, which are seen as logicalView. In my opinion the togglePanel is not the item, which needs the form because it's switch type is client (not server and ajax, which need form tags). But the command link does need the form tag, because if I remove it, an error occurs saying "this link is disabled as it is not nested within a jsf form".
So I tried to replace the commandLink with a commandButton. This worked fine first and the form wasnt necessary anymore. But somehow the behaviour is completely random now. Sometimes the tables can be expanded, sometimes nothing happens when i click the expand button. When i add form tags again, it works fine, but doesnt solve my ViewExpiredException.
Hope, somebody can help me out here...
Thanks for your help!
Buntspecht

If you only need to switch the panel you can use <a4j:commandLink> that lets you limit the execution scope (so it won't submit the whole form). Or you can remove the command components altogether and just use JavaScript API of the togglePanel:
<a onclick="#{rich:component('panelId')}.switchToItem(#{rich:component('panelId')}.nextItem())">Next</a>

Thank you very much for your help Makhiel. I finally managed to solve the problem with the commandButtons solution. I can't explain why, but the IDs of my togglePanelItems got duplicated in the different reports.
Giving every togglePanelItem a unique ID like
<rich:togglePanelItem name="closed" id="#{param.reportWrapper}_opened">
and
<rich:togglePanelItem name="opened" id="#{param.reportWrapper}_closed">
solved the problem...
So now we got rid of all the h:form tags and thus have about 50 logical views less! :)

Related

Dynamic menu in JSF

I am trying to implement a dynamic menu with JSF2/RichFaces. The idea is simple, the menu is defined in the database, and depends on the connected user.
The menu is correctly generated, but the action is not working, and I don't understand why.
This is my JSF code:
<c:forEach items="${sessionMenu.menus}" var="menu"><!-- Menus -->
<li>
${menu.label}
<ul class="sub-menu" id="sub-${menu.label}">
<c:forEach items="${menu.submenus}" var="submenu"><!-- Sub menus -->
<li>
<a4j:commandLink value="${submenu.label}" action="${sessionMenu.action}"/>
</li>
</c:forEach>
</ul>
</li>
</c:forEach>
${sessionMenu.action} returns a string which should redirect to another page (defined as a navigation-case in the faces-config file).
I also tried to replace the action with a hard-coded string, and it works, I guess that the problem arrives with the EL which is not correctly interpreted.
If some one can help me to understand what is wrong?
Edit:
Here is an example of the links generated:
<a id="mainForm:j_id_g_0_2_0_2" onclick="RichFaces.ajax("mainForm:j_id_g_0_2_0_2",event,{"incId":"1"} );return false;" name="mainForm:j_id_g_0_2_0_2" href="#">Screen</a>
This one was generated dynamically and it does nothing;
<a id="mainForm:j_id_g_0_2_0_4" onclick="RichFaces.ajax("mainForm:j_id_g_0_2_0_4",event,{"incId":"1"} );return false;" name="mainForm:j_id_g_0_2_0_4" href="#">Screen</a>
And this one used a static string and the redirection is working properly.
These 2 links were generated in the same page and on the same request.
Finally, I got it working, I just changed the action value to the method name as it is defined in my bean, that means:
<a4j:commandLink value="#{submenu.label}" action="#{sessionMenu.getAction()}" />
If some one can explain why, I am curious.

Access JSF Custom Tag's id

TL;DR
Is there something like composite's cc.clientId that will give me the id of a custom tag?
Details:
I'd like a custom tag that will render a label, a value, and an icon. When the icon is clicked, a Bootstrap modal is supposed to open up that will edit the value.
<ui:composition>
<div> #{field.label}: #{field.value}
<a class="icon-pencil" data-target="#editForm" data-toggle="modal">
</div>
<h:form id="editForm" class="modal hide fade" role="dialog" ...>
... there's an input here linked to field.value ...
</h:form>
</ui:composition>
I can use the tag with <my:editor />. When I do so within a ui:repeat, an id is prepended to the editForm so that it renders with id j_idt104:editForm. So I need to modify the data-target to include the id.
This would be really easy with a composite component because I have access to the id via cc.clientId:
data-target="\##{cc.clientId}\:editForm"
However, I can't get it to work with a custom tag because I don't know of an EL expression (or something) that will give me access to the id. I could probably wait until after the page is loaded, then use jQuery to inspect the id and set the data-target after the fact, but I was hoping for a more pure-JSF solution.
I'm using JSF 2.1 and Bootstrap 2.3, at the moment.
Answer seems to be no. BalusC says you should replace things like custom tags with "normal" JSF components. In this case, that would mean using the composite component.
If you're deadset on using custom tag, I worked around the issue (with lots of help from the answer here) by using an index in the form's id:
I replaced the ui:repeat with a c:forEach that has access to the item's index:
<table>
<c:forEach items="#{bean.items}" var="item" varStatus="status">
<my:editor index="#{status.index}" ... />
</c:forEach>
</table>
And in the custom tag, I used the index in the data-target:
<tr>
<td>#{label}: #{value}</td>
<td>
<a data-target="\#editForm-#{index}" ... ></a>
<h:form id="editForm-#{index}" ... >
...
</h:form>
</td>
</tr>
The only catch is that c:forEach is a build-time construct (see details in this answer), which is a problem if you need any render-time data (like if you build information up in preRenderView). If you do, then you're better off using ui:repeat with a custom component, and relying on the cc.clientId.

Disabling items in JavaServer Faces

I’m new in JavaServer Faces.
I have a simply form:
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">#{msgs.leavereply}</h3>
<h1>#{!forumsBean.activeWithProject}</h1>
<h:form>
<h:inputTextarea value="#{forumpostsBean.text}" style="resize:none"/>
<h:commandButton value="#{msgs.add}" action="#{forumpostsBean.addForumpost}"/>
</h:form>
</div>
By clicking command button all it’s fine, executes method forumpostsBean.addForumpost.
But when I modify code to this:
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">#{msgs.leavereply}</h3>
<h1>#{!forumsBean.activeWithProject}</h1>
<h:form>
<h:inputTextarea value="#{forumpostsBean.text}" style="resize:none" disabled="#{not forumsBean.active}"/>
<h:commandButton value="#{msgs.add}" action="#{forumpostsBean.addForumpost}" disabled="#{not forumsBean.active}"/>
</h:form>
</div>
When items not disabled, current page only have refreshed, but method forumpostsBean.addForumpost don’t executes.
In both variants, <h1>#{!forumsBean.activeWithProject}</h1> shows correct value.
You have 2 beans? one called forumpostsBean and another just forumsBean? I can't see if you've got them both but maybe its the main error? as the comment above says, maybe your logic is out of place.
You may want to use rendered="" on the button rather that just disabling it so its not part of the dom at all upon page load and then use an ajax call when you type data in your input field to do partial ajax render the div again and show it. good for testing.

Conditional rendering in JSF [duplicate]

This question already has an answer here:
Ajax update/render does not work on a component which has rendered attribute
(1 answer)
Closed 7 years ago.
Hello I have this code to conditionally render components in my page:
<h:commandButton action="#{Bean.method()}" value="Submit">
<f:ajax execute="something" render="one two" />
</h:commandButton>
<p><h:outputFormat rendered="#{Bean.answer=='one'}" id="one" value="#{messages.one}"/></p>
<p><h:outputFormat rendered="#{Bean.answer=='two'}" id="two" value="#{messages.two}"/></p>
It gets the answer and renders the component but in order to see it on my page, I need to refresh the page. How can I fix this problem? Any suggestions?
The JSF component's rendered attribute is a server-side setting which controls whether JSF should generate the desired HTML or not.
The <f:ajax> tag's render attribute should point to a (relative) client ID of the JSF-generated HTML element which JavaScript can grab by document.getElementById() from HTML DOM tree in order to replace its contents on complete of the ajax request.
However, since you're specifying the client ID of a HTML element which is never rendered by JSF (due to rendered being false), JavaScript can't find it in the HTML DOM tree.
You need to wrap it in a container component which is always rendered and thus always available in the HTML DOM tree.
<h:commandButton action="#{Bean.method()}" value="Submit">
<f:ajax execute="something" render="messages" />
</h:commandButton>
<p>
<h:panelGroup id="messages">
<h:outputFormat rendered="#{Bean.answer=='one'}" value="#{messages.one}"/>
<h:outputFormat rendered="#{Bean.answer=='two'}" value="#{messages.two}"/>
</h:panelGroup>
</p>
Unrelated to the concrete problem, you've there a possible design mistake. Why would you not just create a #{Bean.message} property which you set with the desired message in the action method instead, so that you can just use:
<h:commandButton action="#{Bean.method()}" value="Submit">
<f:ajax execute="something" render="message" />
</h:commandButton>
<p>
<h:outputFormat id="message" value="#{Bean.message}" />
</p>
I know it's not the central point of the question, but as I had this problem many times in the past, I just post it here to help others who are in need.
For those who uses PrimeFaces there's a component in PrimeFaces Extension called Switch.
Sometimes you need to display different outputs or components depending on a value. Usually you can achieve this by using the ui:fragment tag. With the pe:switch util tag you won't have to declare ui:fragment tags, with different checks like ui:fragment rendered="#{!empty someController.value}", anymore.
style="visibility: #{questionchoose.show==false ? 'hidden' : 'visible'}"

JSF navigate to external url using image button

Simple question (using JSF 2.0 and primefaces 2.2.1):
I need to create a button or link that will take me to an external url (i.e. www.facebook.com) and I need that button to look like the facebook icon instead of having the literal word. How can I do this? Thank you.
You basically want to end up with the following in the JSF-generated HTML code:
<a><img /></a>
There are several ways how to achieve this in JSF.
Just do it:
<a href="http://www.facebook.com">
<img src="#{request.contextPath}/resources/images/facebook.png" />
</a>
Use <h:graphicImage>:
<a href="http://www.facebook.com">
<h:graphicImage name="images/facebook.png" />
</a>
Eventually, with <h:outputLink>:
<h:outputLink value="http://www.facebook.com">
<h:graphicImage name="images/facebook.png" />
</h:outputLink>
What way to choose depends on whether you really need it to be a JSF component. E.g. in order to be able to grab/manupulate it in backing bean, and/or to re-render by ajax, etc.

Resources