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.
Related
I have a big form that I need to reuse in multiple pages. So, I decided to create a
<ui:composition> that contains the form and include it in some pages (page1.xhtml and page2.xhtml).
form.xhtml:
<ui:composition ...>
<!-- The form goes here -->
</ui:composition>
This form has a controller called FormController.
In page1.xhtml and page2.xhtml I just include the form using a <ui:include> tag:
<ui:include src="/WEB-INF/.../form.xhtml"/>
I need to initialize a property in the FormController bean, so, in page1.xhtml I decided to set an attribute with the Id that I need (for example 5):
<c:set var="id" scope="request" value ="5"/>
And in the controller I just get the value of this attribute:
#PostConstruct
public init() {
Long id = ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getAttribute("id");
//Do some queries to the database
}
Until know, everything works fine. But in page2.xhtml the "initialization" of the bean property has to be done after an ajax request, so I used the following code:
<h:selectOneMenu ...>
<f:selectItems ...>
<f:ajax listener="#{otherBean.doSomething}" render="panel"/>
</h:selectOneMenu>
<h:panelGroup id="panel">
<c:set var="id" scope="request" value ="#{otherBean.id}"/>
<ui:include src="/WEB-INF/.../form.xhtml"/>
</h:panelGroup>
What is weird is that this works just the first time I select an element in the <h:selectOneMenu>. The second time, the doSomething() method is called but the panel is not rendered (I don't know why, you know why?), so I decided to explore the following alternative that works well in both pages, but I feel that it isn't a good solution:
#{bean.init(otherBean.id)}
<ui:include src="/WEB-INF/modules/company/company.xhtml"/>
As you see, I am just calling an init method (before the <ui:include>) with the argument I need. In the controller I just set the property and do the corresponding queries:
public init(Long id) {
this.id = id;
//Do some queries
}
What do you thing about this solution?
If the form has to be initialized at start, you can use
<f:metadata>
<f:viewAction action="#{otherBean.initSomething('MYID2')}"/>
</f:metadata>
If the form has to be initialized by an action
<h:commandButton action='#{otherBean.doSomething('MYID1')}'...>
or
<f:ajax listener="#{otherBean.doSomething('MYID')}" .../>
I am seeing different behaviors of and in a page containing multiple forms.
Here is my backing bean:
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class MultiFormBean
{
String inputText1 = "";
String inputText2 = "";
#PostConstruct
public void initializeBean(){
System.out.println("PostConstruct Called ------------------");
}
public String getInputText1()
{
return inputText1;
}
public void setInputText1(String inputText1)
{
this.inputText1 = inputText1;
}
public String getInputText2()
{
return inputText2;
}
public void setInputText2(String inputText2)
{
this.inputText2 = inputText2;
}
public void doSubmit1() {
inputText2 = inputText1;
}
public void doSubmit2() {
inputText1 = inputText2;
}
}
When i use the following xhtml , clicking Submit1 and Submit2 any number of times won't call #PostConstruct more than once:
<h:body>
<h:form id="firstForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget1"/>
<h:inputText id="first_input" value="#{multiFormBean.inputText1}"/>
<h:commandButton id="click1" action="#{multiFormBean.doSubmit1}" value="submit1" type="submit"
onclick="javascript:jsf.ajax.request(this, event, {execute:'firstForm', render:'renderTarget1 secondForm'}); return false;">
</h:commandButton>
</h:form>
<h:form id="secondForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget2"/>
<h:inputText id="second_input" value="#{multiFormBean.inputText2}"/>
<h:commandButton id="click2" action="#{multiFormBean.doSubmit2}" value="submit2" type="submit"
onclick="javascript:jsf.ajax.request(this, event, {execute:'secondForm', render:'renderTarget2 firstForm'}); return false;">
</h:commandButton>
</h:form>
</h:body>
But the following xhtml would call #PostConstruct more than once:
<h:body>
<h:form id="firstForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget1"/>
<h:inputText id="first_input" value="#{multiFormBean.inputText1}"/>
<a4j:commandButton id="click1" action="#{multiFormBean.doSubmit1}" value="submit1" type="submit" execute="#form" render="renderTarget1,secondForm"/>
</h:form>
<h:form id="secondForm" prependId="false">
<h:panelGroup layout="block" id="renderTarget2"/>
<h:inputText id="second_input" value="#{multiFormBean.inputText2}"/>
<a4j:commandButton id="click2" action="#{multiFormBean.doSubmit2}" value="submit2" type="submit" execute="#form" render="renderTarget2,firstForm"/>
</h:form>
</h:body>
Please can anyone help me use the <a4j:commandButton> instead of <h:commandButton>
Also i see that i cannot call the method doSubmit2() with a4j commandButton
I think that problem here is in bug inside JSF2 and Richfaces4. From 4 version Richfaces started using JSF embedded ajax capabilities. And There is a bug with using multiple forms on page with ajax requests. The problem there that richfaces renders special hidden input with the id of currently rendered view state. This id is changed when new view is rendered. And it is also submitted with every request to show that it belongs to some specific view. So when you have multiple forms on the same page after first ajax request the view state is getting the wrong place and it can be not submitted again second time. Sometimes behavior looks like very very wierd with no logical description.
PostConstruct is called twice because server thinks that two requests belong to different views(view state is not sumbitted) and as far as bean is view scoped it is created twice. After clicking aroung ajax can completelly stop working with this because server woukd not recognize the view(probably what you see when you can not click second submit button).
In the first place I recommend you to use latest available version of JSF and Richfaces. This bug (and many more) may be already fixed there.
myBean is in request scope.
<h:form id="indexFormID">
<a4j:outputPanel ajaxRendered="true" layout="block">
<h:inputText id="inputForHD" value="#{myBean.inputParam}"></h:inputText>
<a4j:commandLink value="Submit" action="#{myBean.myMethod}" reRender="renderSuccess" process="indexFormID:inputForHD"></a4j:commandLink>
</a4j:outputPanel>
<h:panelGroup id="renderSuccess">
<h:panelGroup rendered="#{myBean.someBoolean}">
//Some other JSF components go here
</h:panelGroup>
</h:panelGroup>
</h:form>
MyBean class definition:
private String inputParam;
//Getters and setters are there
public String myMethod()
{
log.debug("~ Value of inputParam" +this.getInputParam()); //This is printing null value for inputParam
//when commandLink is clicked
return null;
}
Why my inputParam is not getting set with the input parameters?
Ok I found few issues with your approach:
<h:inputText id="inputForHD" value="#{myBean.inputParam}"></h:inputText>
You are already mapping the inputParam attribute with this bean, why have a new Id "inputForHD"
Use the inputParam itself, if you want to use inputForHD, you can pick the same from request Parameter map like.
String inputForHD = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("indexFormID:inputForHD");
Also as I mentioned previously wrap the output panel inside the and a4j panel e.g.
<h:panelGroup id="renderSuccess">
<h:panelGroup rendered="#{helloWorld.someBoolean}">
//Some other JSF components go here
<h:inputText id="inputForHDasdasd" value="#{helloWorld.inputParam}"></h:inputText>
</h:panelGroup>
</h:panelGroup>
This is working fine, let know if any issues.
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 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>