I have the code bellow:
<c:set var="show" value="#{cartBean.itemsAdded}" />
<c:if test="${show}">
<h:form id="test1">
<h:commandLink action="#{cartBean.foo}">this doesn't work</h:commandLink>
</h:form>
</c:if>
<h:form id="test2">
<h:commandLink action="#{cartBean.foo}">this works!</h:commandLink>
</h:form>
When show=false, show only the second link.
And it works. I can reach server (I'm using debug to see this).
When show=true, both links appears. But ONLY second link works. The link inside conditional doesn't trigger the action in server.
Someone, can, please, help me?
Note: the same thing happens when I use a4j:outputPanel rendered="#{show}"
During processing of the form submit, JSF will re-evaluate whether the command button/link is been rendered. If it is not rendered, then it will simply skip the action.
You need to ensure that the expression #{cartBean.itemsAdded} returns true as well when the form submit is been processed by JSF. An easy test is to put the bean in the session scope (and I assume that the isItemsAdded() is a pure getter, i.e. it contains only return itemsAdded;).
If that did fix the problem and you'd like to keep the bean in the request scope, then add a <a4j:keepAlive> to retain the bean properties in the subsequent request.
<a4j:keepAlive beanName="#{cartBean}" />
See also:
Commandlink is not being invoked
Unrelated to the concrete problem, you should prefer JSF tags/attributes over JSTL ones as much as possible. In this particular case, you should get rid of both JSTL <c:> tags and use the JSF-provided rendered attribute instead:
<h:form id="test1" rendered="#{cartBean.itemsAdded}">
<h:commandLink action="#{cartBean.foo}">this doesn't work</h:commandLink>
</h:form>
WORKARROUND:
I don't want to use sessionScope, because of the danger of use this in a huge system (my case). I don't like to use keepAlive neighter, because I'm in a clutered server and many attributes are not serializable.
Anyway, I've found this workarround:
Send a parameter in request (like show=true)
Change the check method, adding a OR in return to see this new parameter.
MANAGED BEAN:
Before:
public boolean itemsAdded() {
return foo; // my initial check
}
After:
public HttpServletRequest getRequest() {
return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
}
public boolean itemsAdded() {
return foo || getRequest().getParameter("show") != null;
}
XHTML:
Before:
<c:set var="show" value="#{cartBean.itemsAdded}" />
<c:if test="${show}">
<h:form id="test1">
<h:commandLink action="#{cartBean.foo}">link</h:commandLink>
</h:form>
</c:if>
After:
<c:set var="show" value="#{cartBean.itemsAdded}" />
<c:if test="${show}">
<h:form id="test1">
<h:commandLink action="#{cartBean.foo}">link
<f:param name="show" value="true"/>
</h:commandLink>
</h:form>
</c:if>
IMPROVED (and tiny) WORKARROUND:
Change only XHTML:
Before:
<c:if test="#{cartBean.itemsAdded}">
<h:form id="test1">
<h:commandLink action="#{cartBean.foo}">link</h:commandLink>
</h:form>
</c:if>
After:
<c:if test="#{cartBean.itemsAdded || params['show']}">
<h:form id="test1">
<h:commandLink action="#{cartBean.foo}">link
<f:param name="show" value="true"/>
</h:commandLink>
</h:form>
</c:if>
Related
I am creating a simple page that will, after clicking a button, replace one panelGroup with another. First, the code:
<h:body>
<ui:composition template="/elements/templateWithMenu.xhtml">
<ui:define name="content">
<div class="rightContent">
<h:panelGroup id="lol" rendered="#{test.firstStep.booleanValue()}">
<h3>This should disappear</h3>
<h:form id="newPollForm1" rendered="#{test.firstStep.booleanValue()}">
<fieldset>
<h:commandLink value="Next" action="#{test.firstStepCompleted()}" >
<f:ajax execute="#all" render="lol" />
</h:commandLink>
</fieldset>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{test.secondStep.booleanValue()}">
Works!
</h:panelGroup>
</div>
</ui:define>
</ui:composition>
</h:body>
The backing bean simply sets the firstStep as false, and secondStep as true.
Now, when I tried running this, I got <f:ajax> contains an unknown id 'lol' - cannot locate it in the context of the component j_idt39. After googling a bit, I found out that for elements outside the form's scope, I need to use SEPARATOR_CHAR (:). That didn't work. So I tried messing with different combinations of #{component} and #{cc}, but nothing works. I even found this awesome explanation, but again, I failed miserably. If I use #all, everything goes ok (one panel is replaced with another), but I really need to render a specific component.
Help? Please?
You need to update the common parent <div class="rightContent"> instead. This one is always rendered and thus guarantees that JavaScript/Ajax can access and mainpulate its children. Replace it by <h:panelGroup layout="block"> and give it an id.
<h:panelGroup layout="block" id="content" class="rightContent">
<h:panelGroup rendered="#{test.firstStep}">
<h3>This should disappear</h3>
<h:form id="newPollForm1">
<fieldset>
<h:commandLink value="Next" action="#{test.firstStepCompleted()}" >
<f:ajax execute="#form" render=":content" />
</h:commandLink>
</fieldset>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{test.secondStep}">
Works!
</h:panelGroup>
</h:panelGroup>
Using this Test.java class:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class Test {
private int number = 0;
public void firstStepCompleted() {
number++;
}
public boolean isFirstStep() {
return number == 0;
}
public boolean isSecondStep() {
return number == 1;
}
}
Note that I removed the superfluous Boolean#booleanValue() calls and the duplicated rendered contition on the form.
If that still doesn't work, then the /elements/templateWithMenu.xhtml apparently contains another naming container component which has prepended its ID. For starters who haven't memorized all naming container components yet, an easy way to figure the real right client ID is to open the page in browser, rightclick and View Source and then locate the JSF-generated HTML element and take exactly its id attribute value (and prefix it with : in the <f:ajax render>).
I want to find out the previous page from where the current page is called. Based on the previous page I want to enable or disable a particular component. Can anybody help me in this regard.
Depends on the concrete functional requirement which isn't entirely clear from your question. You could pass an unique request parameter. E.g. when it concerns a GET link:
<h:link value="Next" outcome="next">
<f:param name="foo" value="bar" />
</h:link>
or a POST link:
<h:form>
<h:commandLink value="Next" action="next">
<f:param name="foo" value="bar" />
</h:commandLink>
</h:form>
with in next.xhtml
<h:someComponent rendered="#{param.foo == 'bar'}">
...
</h:someComponent>
or if you don't care about the param's value:
<h:someComponent rendered="#{not empty param.foo}">
...
</h:someComponent>
An alternative which can be much better if you don't want to allow the enduser being able to manipulate the request is to set a bean property during a POST action and then return to the next view:
<h:form>
<h:commandLink value="Next" action="#{bean.next}" />
</h:form>
with e.g.
public String next() {
foo = "bar";
return "next";
}
and in next.xhtml
<h:someComponent rendered="#{bean.foo == 'bar'}">
...
</h:someComponent>
I've been using JSF for a while but there's something that has always confused me. Hopefully someone can help.
Simple example, there's a page that shows a table of "Person"s and when you click on the "Person" name, it takes you to a page to view the details of the "Person".
Typically, I implement a personSearch.jsf page like this:
<h:dataTable value="#{personHandler.persons}" var="person">
<h:column>
<h:commandLink action="#{personHandler.show( person.id )}" >
<h:outputText value="#{person.name}" />
</h:commandLink>
</h:column>
</h:dataTable>
And I implement a personView.jsf page like this:
<h:panelGrid columns="2">
<h:outputText value="Person ID:" />
<h:outputText value="#{personHandler.selectedPerson.id}" />
<h:outputText value="Person Name:" />
<h:outputText value="#{personHandler.selectedPerson.name}" />
</h:panelGrid>
PersonHandler.show(Integer personId) sets personHandler.selectedPerson and then redirects to the personView page.
This all works fine when PersonHandler is a session bean. But I prefer it to be a request scoped bean because the user may have several windows open and I don't want there to be only one selected person per session.
So my question is, what's the "correct" way to do this JSF? I was once able to get what I wanted using a4j:keepAlive on the personHandler, but that always felt like a kludge. Again, this is something I've never understood about JSF.
Any help is greatly appreciated!
rob
If the view is supposed to be bookmarkable, pass the person ID as a GET request parameter instead of a POST request "parameter".
<h:outputLink value="viewperson.xhtml">
<f:param name="id" value="#{person.id}" />
</h:outputLink>
This way you can use two #RequestScoped beans, one for the list and one for the view. You can preload the selected person as follows:
#ManagedProperty(value="#{param.id}")
private Long id;
#PostConstruct
public void init() {
selectedPerson = personDAO.find(id);
}
If it is not supposed to be bookmarkable, then just create a single view which renders the view state conditionally.
<ui:fragment rendered="#{!personHandler.viewMode}">
<h:form>
<h:dataTable value="#{personHandler.persons}" var="person">
<h:column>
<h:commandLink value="#{person.name}" action="#{personHandler.show(person)}" />
</h:column>
</h:dataTable>
</h:form>
</ui:fragment>
<ui:fragment rendered="#{personHandler.viewMode}">
<h:form>
...
<h:commandLink value="Go back" action="#{personHandler.back}" />
</h:form>
</ui:fragment>
(You can if necessary split out the content of the both framgents to another Facelet files which you include by <ui:include>)
This way you can use a single #ViewScoped bean with action methods returning void or null.
public void show(Person selectedPerson) {
this.selectedPerson = selectedPerson;
}
public void back() {
selectedPerson = null;
}
public boolean isViewMode() {
return selectedPerson != null;
}
You can even wrap the whole view in some
<h:panelGroup id="container">
and nest the following in both command links to let Ajax magic do the work
<f:ajax execute="#form" render=":container" />
Related to a previous example, i tried to monitor my get/set methods on the server (when they are called, and how often). So, my actual been look such :
#ManagedBean(name="selector")
#RequestScoped
public class Selector {
#ManagedProperty(value="#{param.profilePage}")
private String profilePage;
public String getProfilePage() {
if(profilePage==null || profilePage.trim().isEmpty()) {
this.profilePage="main";
}
System.out.println("GET "+profilePage);
return profilePage;
}
public void setProfilePage(String profilePage) {
this.profilePage=profilePage;
System.out.println("SET "+profilePage);
}
}
and the only page who can call this method (it only calls the get method on rendered) is :
<!DOCTYPE html>
<ui:composition
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:panelGroup layout="block" id="profileContent">
<h:panelGroup rendered="#{selector.profilePage=='main'}">
// nothing at the moment
</h:panelGroup>
</h:panelGroup>
</ui:composition>
my stupor when i see the server log, and i see :
SET null
GET main
GET main
GET main
GET main
GET main
GET main
GET main
What? It call seven times the getProfilePage() method? (and also 1 time setProfilePage())
I would like to know why this behaviour :)
Thanks
ADDED AN EXAMPLE
Bean
#ManagedBean(name="selector")
#RequestScoped
public class Selector {
#ManagedProperty(value="#{param.profilePage}")
private String profilePage;
#PostConstruct
public void init() {
if(profilePage==null || profilePage.trim().isEmpty()) {
this.profilePage="main";
}
}
public String getProfilePage() { return profilePage; }
public void setProfilePage(String profilePage) { this.profilePage=profilePage; }
}
profile.xhtml
<h:panelGroup layout="block" id="profileContent">
<h:panelGroup layout="block" styleClass="content_title">
Profilo Utente
</h:panelGroup>
<h:panelGroup rendered="#{selector.profilePage=='main'}">
<ui:include src="/profile/profile_main.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{selector.profilePage=='edit'}">
<ui:include src="/profile/profile_edit.xhtml" />
</h:panelGroup>
</h:panelGroup>
// profile_main.xhtml
<h:form id="formProfileMain" prependId="false">
<h:panelGroup layout="block" styleClass="content_span">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<h:panelGroup layout="block" styleClass="profilo_3">
<h:commandButton value="EDIT">
<f:setPropertyActionListener target="#{selector.profilePage}" value="edit" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
</h:panelGroup>
</h:panelGroup>
</h:form>
// profile_edit.xhtml
<h:form id="formProfileEdit" prependId="false">
<h:panelGroup layout="block" styleClass="content_span">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<h:panelGroup layout="block" styleClass="profilo_3">
<h:commandButton value="Edit">
<f:setPropertyActionListener target="#{selector.profilePage}" value="editProfile" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
<h:commandButton value="Back">
<f:setPropertyActionListener target="#{selector.profilePage}" value="main" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
</h:panelGroup>
</h:panelGroup>
</h:form>
In this example, i call the profile_main (as default); After (for example) I call profile_edit (by clicking on EDIT); After, I return to profile_main by clicking Back. Now, if i want to reload profile_edit (EDIT), i need to click many times on that command button. Why?
EL (Expression Language, those #{} things) won't cache the result of the calls or so. It just accesses the data straight in the bean. This does normally not harm if the getter just returns the data.
The setter call is done by #ManagedProperty. It basically does the following:
selector.setProfilePage(request.getParameter("profilePage"));
The getter calls are all done by rendered="#{selector.profilePage == 'some'}" during the render response phase. When it evaluates false the first time, in UIComponent#encodeAll(), then no more calls will be done. When it evaluates true, then it will be re-evaluated six more times in the following sequence:
UIComponent#encodeBegin() - Locates renderer for the begin of component.
Renderer#encodeBegin() - Renders begin of component.
UIComponent#encodeChildren() - Locates renderer for children of component.
Renderer#encodeChildren() - Renders children of component.
UIComponent#encodeEnd() - Locates renderer for end of component.
Renderer#encodeEnd() - Renders end of component.
The component and its renderer verifies during every step if it is allowed to render. During a form submit, if an input or command component or any of its parents has a rendered attribute, then it will also be evaluated during apply request values phase as part of safeguard against tampered/hacked requests.
True, this look like clumsy and inefficient. It was considered the achilles heal of JSF as per spec issue 941. It's been suggested to remove all those repeated checks and stick to the one done in UIComponent#encodeAll(), or to evaluate isRendered() on a per-phase basis. During EG discussion, it became clear the root of the problem is in EL, not in JSF, and that performance could be greatly improved with CDI. So there was no necessity to solve it from JSF spec side on.
If your concern is that the managed property should be checked only once after its setting if it's null or empty, then consider to move it into a method which is annotated with #PostConstruct. Such a method will be called directly after bean's construction and all dependency injection.
#PostConstruct
public void init() {
if (profilePage == null || profilePage.trim().isEmpty()) {
profilePage = "main";
}
}
See also:
Why JSF calls getters multiple times?
you can use CDI Producers methods.
It will be called many times, but the result of first call is cached in scope of the bean and is efficient for getters that are computing or initializing heavy objects!
See here, for more info.
there's a jsf page with a form:
....
<h:form>
<h:commandLink action="#{userBean.logout}" value="Logout" />
</h:form>
....
<ui:repeat value="#{categoryBean.allCategories}" var="c">
....
The categoryBean.allCategories is a call to EJB which is based on a <f:param> from previous page acquired via #ManagedProperty. So when the user clicks on Logout, the whole page evaluates, but without the param and there's NullPointerException. Is there any possibility how to skip the evaluation?
Make use of the rendered attribute:
<h:panelGroup rendered="#{not empty param.name}">
<ui:repeat value="#{categoryBean.allCategories}" var="c">
...
</ui:repeat>
</h:panelGroup>
Alternatively you can also just do a nullcheck in getAllCategories() method.
if (param != null) {
// Return it.
}
return null;