I was trying to see exactly how property injection works with ViewScoped beans.
I inject the List from One to Two without problem. When I try to inject the same List from Two to Three nothing is injected but I think that's the intended behavior (I might be wrong though).
However when I try to inject the selected value from the SelectOneMenu of Two into Three nothing is being injected.
Is there something I am missing or is that the normal behaviour? If so, how can I retrieve that value in Three?
One.java
#ManagedBean
#ViewScoped
public class One implements Serializable {
private List<String> oneList;
#PostConstruct
void init() {
setOneList(new ArrayList<String>());
getOneList().add("aaa");
getOneList().add("bbb");
getOneList().add("ccc");
getOneList().add("ddd");
}
//Getters + setters...
}
one.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition template="/WEB-INF/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<ui:define name="header">
<h:outputText value="ONE" />
</ui:define>
<ui:define name="content">
<h:form>
<p:commandButton value="two" action="two" ajax="false" />
</h:form>
</ui:define>
</ui:composition>
Two.java
#ManagedBean
#ViewScoped
public class Two implements Serializable {
#ManagedProperty("#{one.oneList}")
private List<String> oneList;
private String twoChoice;
//Getter + setters...
}
two.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition template="/WEB-INF/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<ui:define name="header">
<h:outputText value="TWO" />
</ui:define>
<ui:define name="content">
<h:form>
<p:selectOneMenu id="test" value="#{two.twoChoice}">
<f:selectItems value="#{two.oneList}" />
</p:selectOneMenu>
<p:commandButton value="three" action="three" ajax="false" />
</h:form>
</ui:define>
</ui:composition>
Three.java
#ManagedBean
#ViewScoped
public class Three implements Serializable {
#ManagedProperty("#{two.oneList}")
private List<String> oneList;
#ManagedProperty("#{two.twoChoice}")
private String twoChoice;
private String threeChoice;
//Getters + setters...
}
three.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition template="/WEB-INF/template.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<ui:define name="header">
<h:outputText value="THREE" />
</ui:define>
<ui:define name="content">
<h:outputText value="#{three.twoChoice}" />
</ui:define>
<h:form>
<p:selectOneMenu value="#{three.threeChoice}">
<f:selectItems value="#{three.oneList}" />
</p:selectOneMenu>
</h:form>
</ui:composition>
Managed properties are not intended to work like that. Keep in mind that a #ViewScoped bean is designed to keep alive as long as the view doesn't change, which means at the moment you navigate from one page to another using the non-ajax command button (in fact you're specifying the navigation-case to go in the action attribute), they should be destroyed, so you can't get any value from them.
Normally, I use #ManagedProperty notation to inject broader scope values (for example, a session value in a view scoped bean). So what is the solution for your case?
Actually you have different options:
You can use <f:viewParam /> tag to send the GET parameters while changing the view.
You can use flash scope, which keeps your values in a map that survives to a redirection. Not recomended if you are using Mojarra implementation of JSF, they still have to fix some problems with them.
Take care of the params yourself and set them in some data structure in session scope, after, when you recover them in your destination bean you can remove them.
Related
This question already has answers here:
How to choose the right bean scope?
(2 answers)
Closed 4 years ago.
I have an application with a p:selectOneMenu component. This component is used to determine what category of file is being uploaded so I can do some work to it when the file is uploaded.
I implemented the answer from this post and it seems to call my setter methods correctly for fileType. But once the file is submitted and the handleFileUpload method is called, the fileType getter method returns null.
For example, if I select Foo then I get the output
File type changed to: Foo
But when I hit the upload button I get the output
The file type selected is null
When I expect
The file type selected is Foo
What is causing the get method to return two different results and is there a way to fix this?
main.xhtml
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<meta http-equiv="refresh"
content="#{session.maxInactiveInterval};url=index.xhtml" />
<f:view contentType="text/html">
<h:head>
<title>File Upload</title>
</h:head>
<p:layout fullPage="true">
<p:layoutUnit position="center">
<ui:insert name="pagebody" />
</p:layoutUnit>
</p:layout>
</f:view>
</html>
index.xhtml
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui" template="/templates/main.xhtml">
<ui:define name="pagebody">
<h:body>
<h:form id="uploadform" enctype="multipart/form-data">
<h:panelGrid columns="2" style="margin-bottom:10px" cellpadding="5">
<h:outputText value="File Type:" />
<p:selectOneMenu value="#{uploadBean.fileType}">
<f:selectItem itemLabel="Foo" itemValue="Foo"/>
<f:selectItem itemLabel="Bar" itemValue="Bar"/>
<f:ajax listener="#{uploadBean.changeFileType}" />
</p:selectOneMenu>
</h:panelGrid>
<br />
<p:fileUpload fileUploadListener="#{uploadBean.handleFileUpload}" mode="advanced"/>
</h:form>
</h:body>
</ui:define>
</ui:composition>
UploadBean.java
#RequestScoped
#ManagedBean(name = "uploadBean")
public class UploadBean implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String fileType = null;
#PostConstruct
public void init() {
}
public void handleFileUpload(FileUploadEvent event){
System.out.println("The file type selected is " + this.getFileType());
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public void changeFileType() {
System.out.println("File type changed to: " + this.getFileType());
}
}
As pointed out by #Kukeltje the issue was that my bean was not properly scoped. Switching from RequestScoped to ViewScoped fixed the issue I was having.
If I pass an expression to a composite component like
<My:myButton action="#{bean.myaction()" value="#{bean.buttText()}"
and try to use it in
<cc:implementation>
<h:commandButton actionListener="#{cc.attrs.action} value="#{cc.attrs.value}...
I get the exception "Target Unreachable, identifier 'bean' resolved to null".
But only if bean is a <ui:param name="bean" value="#{myRealBean}"/> inside a template. The error only occours with actionListener. The button text, resolved from the same bean in the same way will be shown.
This old question seems to have the same problem but no answer.
If I split the parameter to
<My:myButton bean="#{bean}" method="myaction" value="#{bean.buttText()}"
and use
<cc:implementation>
<h:commandButton actionListener="#{cc.attrs.bean[cc.attrs.method]()} ...
no error occours and everything works fine.
How can I use a function-expression in an actionListener inside a composite component, where the bean is a <ui:param?
Glassfish 4.1.1, Mojarra 2.2.12
And now, the MCVE
a bean:
package beans;
import java.io.Serializable;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class Testbean implements Serializable {
public String getButtText() { return "Submit"; }
public void doAction() { System.out.println("Ajax was here."); }
}
a page
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ST="http://java.sun.com/jsf/composite/softeam" >
<h:head>
</h:head>
<h:body>
<h:form id="testform">
<h:panelGrid columns="1">
<ui:include src="testtemplate.xhtml">
<ui:param name="bean" value="#{testbean}"/>
</ui:include>
<ST:testButt text="#{testbean.buttText}" action="#{testbean.doAction()}"/>
<h:commandButton value="#{testbean.buttText}" actionListener="#{testbean.doAction()}"/>
</h:panelGrid>
</h:form>
</h:body>
</html>
and a template
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:ST="http://java.sun.com/jsf/composite/softeam">
<ST:testButt text="#{bean.buttText}" action="#{bean.doAction()}"/>
<h:commandButton value="#{bean.buttText}" action="#{bean.doAction()}" />
</ui:composition>
and the component
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:S="http://xmlns.jcp.org/jsf/composite" >
<S:interface>
<S:attribute name="action" method-signature="void action()"/>
<S:attribute name="text" type="java.lang.String"/>
</S:interface>
<S:implementation>
<h:commandButton style="display:inline-block"
value="#{cc.attrs.text}"
actionListener="#{cc.attrs.action}"
/>
</S:implementation>
</html>
All buttons will be shown (with the bean.buttText()) but only the second, third and fourth will call the action. The first button throws an exception when clicked.
Edit: Example reduced to JSF, no PrimeFaces components.
This question already has answers here:
Creating master-detail pages for entities, how to link them and which bean scope to choose
(2 answers)
Closed 6 years ago.
I am using JSF 2.0 and have a simple TestButton.xhtml like below:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<h:body>
<h:form>
<h:inputText value="#{employee.empName}"/><br/>
<h:commandButton value="Derive" action="EmployeeTest2" >
<f:param name="Name" value="#{employee.empName}"/>
</h:commandButton>
</h:form>
</h:body>
</ui:composition>
And a Managed bean which is view scoped.
#ManagedBean(name="employee", eager=true)
#ViewScoped
public class Employee implements Serializable{
String empName;
String selectedEmployeeName;
public String getSelectedEmployeeName() {
return selectedEmployeeName;
}
public void setSelectedEmployeeName(String selectedEmployeeName) {
this.selectedEmployeeName = selectedEmployeeName;
}
public Employee() {
super();
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public void manipulate(){
System.out.println("Manipulate called...");
System.out.println(selectedEmployeeName);
this.selectedEmployeeName = this.selectedEmployeeName + " Jr.";
}
Now on click of button, i navigate to EmployeeTest2.xhtml which has following code :
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<f:view>
<f:metadata>
<f:viewParam name="Name" value="#{employee.selectedEmployeeName}"/>
<f:event type="preRenderView" listener="#{employee.manipulate}"/>
</f:metadata>
</f:view>
<h:body>
<h:inputText value="#{employee.selectedEmployeeName}"/>
</h:body>
</ui:composition>
i get correctly navigated to EmployeeTest2.xhtml but my parameter value is coming as null. I can see that manipulate method is getting called(sysouts get printed) but selectedEmployeeName is null.
Please advise as to what am i missing.
I already referred to below thread and did same but of no help.
https://stackoverflow.com/questions/20880027/passing-parameters-to-a-view-scoped bean-in-jsf
Update 1: Changed
<h:inputText value="Chris"/>
to
<h:inputText value="#{employee.empName}"/>
Put a breakpoint at setter method for selectedEmployee. Setter is not getting called which means param passing is not working.
Update 2 Please see updated testButton.xhtml
?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<h:body>
<h:form>
<h:inputText name = "Name" value="#{employee.empName}"/><br/>
<h:commandButton value="Submit" action="EmployeeTest2" >
<f:param name="Name" value="#{employee.empName}"/>
</h:commandButton>
</h:form>
</h:body>
</ui:composition>
The code seems correct, in my opinion you are sending null to the next page because the value of the employee.empName is actually null.
I would debug the getEmpName() method when the TestButton.xhtml is loaded and then additionally check, after you click the button and move on to the next page whether the parameter is present in the url.
UPDATE
Ok the param is actually the value of the input. In that case add name to its definition. You form should look like this:
<h:inputText name="Name" value="#{employee.empName}"/><br/>
<h:commandButton value="Derive" action="EmployeeTest2" />
UPDATE 2
This is a workaround but you can also try this:
#PostConstruct
public void postConstruct(){
Map<String,String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
setSelectedEmployeeName(params.get("Name"));
}
I'd like to create a master-detail screen with request params and requestScoped beans but the view param doesn't get filled.
The link that invokes the redirect:
<h:form>
<p:dataTable var="visit" value="#{visitBean.findAllVisits()}">
<p:column headerText="mdh">
<p:commandLink action="#{visitDetailBean.seeVisitDetails(visit)}">
<h:graphicImage library="images" name="details.png"/>
</p:commandLink>
</p:column>
....
The method behind it:
public String seeVisitDetails(Visit visit) throws IOException {
return "/pages/mdh-details.xhtml?visitId=" + visit.getId()+ ";faces-redirect=true";
}
The details xhtml page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core">
<f:metadata>
<f:viewParam name="visitId" value="#{visitDetailBean.currentVisitId}" />
</f:metadata>
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="content">
<h:outputText value="#{visitDetailBean.currentVisit.name}"/>
test
</ui:define>
</ui:composition>
and last the details Bean:
private long currentVisitId;
public void setCurrentVisitId(long currentVisitId) {
this.currentVisitId = currentVisitId;
}
public long getCurrentVisitId() {
return currentVisitId;
}
public Visit getCurrentVisit() {
return visitService.findVisit(currentVisitId);
}
currentVisitId is always 0.. I actually can't really find it.
When using templating, anything outside <ui:composition> and <ui:define> is ignored. This includes <f:metadata>.
Move it to inside an <ui:define> of the <ui:composition>.
E.g.
<ui:composition template="/templates/masterLayout.xhtml">
<ui:define name="metadata">
<f:metadata>
<f:viewParam ... />
</f:metadata>
</ui:define>
<ui:define name="content">
...
</ui:define>
</ui:composition>
See also:
When using <ui:composition> templating, where should I declare the <f:metadata>?
Unrelated to the concrete problem, you'd better place masterLayout.xhtml in /WEB-INF folder to prevent direct access.
<ui:composition template="/WEB-INF/templates/masterLayout.xhtml">
See also:
Which XHTML files do I need to put in /WEB-INF and which not?
I have the following page:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
xmlns:thehub="http://java.sun.com/jsf/composite/components/thehub"
template="/templates/masterTemplate.xhtml">
<f:metadata>
<f:viewParam
id="returnToViewParam"
name="returnTo"
value="#{loginMB.returnTo}"
required="true" />
<f:viewParam
id="oauth_verifierViewParam"
name="oauth_verifier"
value="#{loginMB.oauth_verifier}" />
<f:viewParam
id="oauth_tokenViewParam"
name="oauth_token"
value="#{loginMB.oauth_token}" />
<f:event
type="preRenderView"
listener="#{loginMB.preRenderView()}" />
</f:metadata>
<ui:define name="body">
<o:form
id="loginForm"
includeViewParams="true">
<div class="form-vertical well">
<h4>New Users</h4>
<h5>
<h:link outcome="signup">Click here to create an account</h:link>
</h5>
<hr />
<h4>Existing Users</h4>
<h:commandButton
id="googleLoginCommandLink"
styleClass="btn"
action="#{loginMB.redirect()}"
value="Google">
<f:param
name="returnTo"
value="#{param.returnTo}" />
</h:commandButton>
<div class="clearfix"></div>
</div>
</o:form>
</ui:define>
</ui:composition>
And the following bean:
#ManagedBean
#RequestScoped
public class LoginMB implements Serializable {
private static final long serialVersionUID = 1L;
private String returnTo;
public void redirect() {
log.debug("redirect() returnTo:{}", returnTo);
......getter/setters
}
No matter what I do, I can't seem to get returnTo bound once the commandButton is clicked. Since this is a login page, I'd really not like to have LoginMB be a #ViewScoped bean.
Advice? Is there a better way to handle this scenario?
EDIT:
I'm running this on TomEE+ Server v1.5.1 which is served up by MyFaces 2.1.10
Added full page
Clarified the problem: Inside the redirect() function, returnTo is null
Your <f:metadata> is outside <ui:define> and is therefore completely ignored. Add an <ui:insert name="metadata"> to the master template and put the <f:metadata> inside an <ui:define name="metadata"> of the template client.
See also:
When using <ui:composition> templating, where should I declare the <f:metadata>?
Once you fixed that, you can safely remove the <f:param> from the command button. This job is already done by <o:form includeViewParams="true">. If you didn't have it, then the <f:param> would indeed have been mandatory and you'd need to copypaste the same over all command links and buttons in the same form.