Pass parameters from bean to navigated view - jsf

What I do now:
call an action method
<p:commandButton value="Fine"
action="#{dtIndexBean.forwardAction}"
styleClass="ui-priority-primary"
ajax="false">
<f:param name="result" value="fine"/>
</p:commandButton>
in forwardAction method, validate the input and forward to another view if validation goes fine
String result = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("result");
if (result.equals("wrong")) {
System.out.println("WRONG!!");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,
"", "WRONG!!"));
return "";
} else{
paramBean.putForwardParameter("message", "Hello!");
return "forwarded.xhtml";
}
I need to pass some parameters to forwarded view, now I put them in a session bean (here its name is "paramBean") so I can read them in the backing bean of the forwarded view:
String message;
#PostConstruct
private void init(){
message = paramBean.getParameter("message");
paramBean.clearForwardParameter();
}
I only feel that this is clumsy due to the presence of an additional bean. Is there a less clumsy way to do this?

Related

am i using correct way of JSF coding?

I'm new in JSF. Can i use this way of coding instead of using EL in JSF view? and correct me if there is something wrong in my coding or should i use better way.
#Named
#RequestScoped
public class RegistrationBacking extends Root {
#EJB
private UserManagerLocal userManager;
public String register(){
Map<String, Object> parameterMap = getRequestMap();
User user = new User();
user.setUserName((String) parameterMap.get("userName"));
user.setPassword((String) parameterMap.get("password"));
user.setEmail((String) parameterMap.get("email"));
try{
userManager.registerUser(user);
} catch(UserExistsException ex) {
Logger.getLogger(RegistrationBacking.class.getName()).log(Level.SEVERE, null, ex);
getContext().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, getBundle().getString("loginExist"), ex.getMessage()));
return null;
} catch(Exception ex) {
Logger.getLogger(RegistrationBacking.class.getName()).log(Level.SEVERE, null, ex);
getContext().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, getBundle().getString("loginError"), ex.getMessage()));
return null;
}
return "index";
}
}
No. You're basically manually grabbing the submitted values from the request parameter map instead of binding the input values to the model. You're manually filling the model in the controller's action method. You won't be able to perform JSF-managed Conversion and Bean Validation on those inputs.
The right way is the following:
<h:form>
<h:inputText value="#{registrationBacking.user.userName}" />
<h:inputSecret value="#{registrationBacking.user.password}" />
<h:inputText value="#{registrationBacking.user.email}" />
<h:commandButton value="Register" action="#{registrationBacking.register}" />
</h:form>
And then in the backing bean:
private User user;
#PostConstruct
public void init() {
user = new User();
}
public String register {
try {
// ...
}
}
See also:
Passing a JSF2 managed pojo bean into EJB or putting what is required into a transfer object
Creating master-detail pages for entities, how to link them and which bean scope to choose
JSF Service Layer

Best solution to pass objects between two ViewScoped ManagedBeans

I'm wondering what the best practices are to pass data (an object) between two ViewScoped beans.
They need to be view scoped because of the problem that's brilliantly explained here (to put it short: In both views I'm using a h:commandLink from within a h:dataTable which requires the data model to still be present when submitting).
My problem now is that clicking the link also navigates to a new view, so using the following code, my object gets passed but the DetailViewController instance gets killed right after that and a new one is created when the view changes (as you would expect).
View:
<h:dataTable value="#{searchController.dataModel}" var="item">
...
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink id="open" value="open" action="#{searchController.showDetail(item)}" />
</h:column>
</h:dataTable>
Bean:
#ManagedBean
#ViewScoped
public class SearchController {
#ManagedProperty(value="#{detailViewController}")
private DetailViewController detailViewController;
// getters, setters, etc. ...
public String showDetail(Item i) {
detailViewController.setItem(i);
return "view_detail.xhtml";
}
}
How would you solve this? I thought about putting the object inside Flash: FacesContext.getExternalContext.getFlash()... Is there an easier or more elegant solution?
You can use view parameters. (See How do you pass view parameters when navigating from an action in JSF2?)
Typically, your method return the url with query parameters:
public String showDetail(Item i) {
return "view_detail.xhtml?id="+i.getId();
}
And in your view_detail.xhtml file, you add a f:viewParam tag evaluating to on of your bean field:
<f:metadata>
<f:viewParam name="id" value="#{myBean.id}" />
</f:metadata>
Then from your backing bean, you use that field to get your Item instance in your #postConstruct method.
If you don't use the f:viewparam tag, you can also fetch the request parameters to obtain the id.
private String id;
private Item item;
#PostConstruct
public void init() {
if (id != null) {
item = fetchItem(id);
} else {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
Map<String, String> requestParameterMap = externalContext.getRequestParameterMap();
if (requestParameters.containsKey("id")) {
id = requestParameters.get("id");
item = fetchItem(id);
} else {
throw new WebServiceException("No item id in request parameters");
}
}
}

rich:fileUpload more file information

I want to upload many files and I have chosen rich:fileUpload control for this.
My problem is that I need to add more information for each file, for example the title I want to appear in the application for that file. How can I do that, and send to the fileUploadListener method in order to use the id?
Based in your question, the RichFaces FileUpload demo has all the info you need to handle file upload for 1 or more files at the same time.
If you want to add more data (like h:inputText values and others), then you should pass them using valueChangeListener instead value tag attribute, because the fileUploadListener is an event that happens within an ajax call, so your UIComponents won't call the setters for the attributes.
Some code to explain the behavior:
<h:panelGrid cols="2">
<h:outputText value="File Title:">
<h:inputText value="#{fileBean.fileTitle}" immediate="false"
valueChangeListener="#{fileBean.valueChangeFileTitle}" />
<h:outputText value="File:">
<rich:fileUpload
fileUploadListener="#{bean.fileUpload}">
</rich:fileUpload>
</h:panelGrid>
The Bean to handle the requests
public class Bean {
private String fileTitle;
public Bean() {
}
//getters and setters...
public String getFileTitle() {
return this.fileTitle;
}
public void setFileTitle(String fileTitle) {
System.out.println("Calling the setter");
this.fileTitle = fileTitle;
}
public void valueChangeFileTitle(ValueChangeEvent e) {
System.out.println("Calling the ValueChangeListener");
fileTitle = (String)e.getNewValue();
}
//this will be invoked by an ajax call
//the setter of the view won't be invoked for fileTitle
//instead, we should take its value using valueChangeListener
public void fileUpload(UploadEvent ue) {
MyFileManager mfm = MyFileManager.getFileManager();
MyFile myFile = new MyFile();
myFile.setTitle(this.fileTitle);
myFile.setName(ue.getUploadItem().getFileName());
myFile.setData(ue.getUploadItem().getData());
mfm.createFile(myFile);
}
}
Also, avoid to use System.out.println calls in your code, I'm doing it so you can understand what method will be called, instead use a Logger like Log4j.

Session Scoped Managed Bean constructor being called on each page refresh

I am using a session scoped managed bean for handling login in a Java EE application. After I authenticate the user, the user object is saved in this session bean. However, after I refresh the page, the session bean values are gone.
I was debugging the code and it results that the constructor of the session scoped managed bean is called again on page refresh, therefore initializing the user object with a new user. I guess this is not a normal behavior since it should be preserved on the session shouldn't it?
I am posting some parts of the login managed bean including the parameters and the login method. Basically the enteredEmail and enteredPassword stand for the entered data on the login form. If the authentication succeeds, the loggedIn boolean is turned to true and the logged in user object is stored in the checkedUser variable.
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class LoginController implements Serializable {
#EJB
private LoginSessionBean loginSessionBean;
#EJB
private LecturerFacade lecturerFacade;
private Lecturer checkedUser;
private String enteredEmail;
private String enteredPassword;
private boolean loggedIn;
/** Creates a new instance of loginController */
public LoginController() {
loggedIn = false;
checkedUser = new Lecturer();
}
public String login(){
RequestContext context = RequestContext.getCurrentInstance();
FacesMessage msg = null;
this.setCheckedUser(lecturerFacade.findLecturerByEmail(enteredEmail));
if(loginSessionBean.checkPassword(checkedUser, enteredPassword))
{
loggedIn = true;
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Welcome", checkedUser.getFirstName()+ " " + checkedUser.getLastName());
FacesContext.getCurrentInstance().addMessage(null, msg);
context.addCallbackParam("loggedIn", loggedIn);
}
return "Index";
I am also posting the two EJBs that the above managed bean uses. The lecturerFacade retrieves the user object with the entered email, while the loginSessionBean checks the password.
#Stateless
public class LecturerFacade extends AbstractFacade<Lecturer> {
#PersistenceContext(unitName = "EffectinetWebPU")
private EntityManager em;
Logger logger = Logger.getLogger("MyLog");
FileHandler fh;
protected EntityManager getEntityManager() {
return em;
}
public LecturerFacade() {
super(Lecturer.class);
}
public Lecturer findLecturerByEmail(String email) {
try {
return (Lecturer) this.getEntityManager().createQuery("SELECT l FROM Lecturer l WHERE l.email = :email").setParameter("email", email).getSingleResult();
} catch (NoResultException e) {
System.err.println("Caught NOResultException: "+ e.getMessage());
return null;
} catch (NonUniqueResultException e) {
System.err.println("Caught NonUniqueResultException: "+ e.getMessage());
return null;
} catch (IllegalStateException e) {
System.err.println("Caught IllegalStateException: "+ e.getMessage());
return null;
}
}
_
#Stateless
public class LoginSessionBean {
// Add business logic below. (Right-click in editor and choose
// "Insert Code > Add Business Method")
#PersistenceContext(unitName = "EffectinetWebPU")
private EntityManager em;
protected EntityManager getEntityManager() {
return em;
}
public void setEntityManager(EntityManager em) {
this.em = em;
}
public boolean checkPassword(Lecturer user, final String enteredPassword) {
if (user.getPassword().equals(enteredPassword)) {
return true;
} else {
return false;
}
}
}
Please if someone has any suggestion of what is going wrong, please tell me
Im using glassfish 3.1 as application server and Primefaces as JSF library. Also, I have checked and the imported the sessionScoped annotation from the right package and not from javax.enterprise...
Your problem is thus here:
<p:menuitem value="Logout" ... onclick="#{loginController.logout()}"/>
The onclick attribute should represent a JavaScript handler function which is to be executed in the webbrowser when the enduser clicks the element. Something like
onclick="alert('You have clicked this element!')"
The onclick attribute also accepts a ValueExpression, so you can even let JSF/EL autogenerate its value accordingly:
onclick="#{bean.onclickFunction}"
with
public String getOnclickFunction() {
return "alert('You have clicked this element!')";
}
All the EL is thus evaluated when the page is rendered. In your particular case, the logout() method is called everytime the EL is evaluated and thus you're invalidating the session everytime the page is rendered!
You need to bind it to an attribute which takes a MethodExpression like <h:commandLink action>, <h:commandButton action> and in this particular case <p:menuitem action>.
<p:menuitem value="Logout" ... action="#{loginController.logout()}"/>
This can be understood by understanding basic HTML and JavaScript concepts and keeping in mind that JSF ultimately produces HTML/CSS/JS. Open the JSF page in webbrowser, rightclick and View Source to realize it.
Well I managed to solve it today. This was the problem, although I cannot explain why:
I was using Primefaces 3.2 as JSF library so this was the main menu of the index page.
<h:form>
<p:menubar >
<p:menuitem id="registerLink" value="Register" rendered="#{!loginController.loggedIn}" onclick="registerDialog.show()" />
<p:menuitem id="loginLink" value="Login" rendered="#{!loginController.loggedIn}" onclick="loginDialog.show()" />
<p:submenu label="Units" rendered="true">
<p:menuitem id="addNew" value="Add New" onclick="createUnitDialog.show()" />
<p:menuitem id="myUnits" value="My Units" onclick="" />
</p:submenu>
<p:menuitem id="results" value="Results/Statistics" rendered="#{loginController.loggedIn}" onclick=""/>
<p:menuitem id="profile" value="My Profile" rendered="#{loginController.loggedIn}" onclick=""/>
<p:menuitem id="logout" value="Logout" rendered="#{loginController.loggedIn}" onclick="#{loginController.logout()}"/>
</p:menubar>
</h:form>
After setting breakpoints to the whole code I discovered that the logout() method, which is supposed to destroy the managed bean, was called on every page refresh. I don't know why this happened as it should be called when the logout menuitem was clicked.
However, after changing the onclick="#{loginController.logout()} with action="#{loginController.logout()} the problem was solved.
I checked the documentation of Primefaces but nowhere this behavior was explained

JSF Dynamic Rendered Component's Value becomes null when the form is submitted

I have a JSF page which renders a text field depending on the value of a drop down using primefaces ajax listner. The dynamic rendering is done fine. but the problem is once I submit the form the the bound value of that textfield doesn't get bound instead it is shown as null.
this is the part of my JSF only the necessary fields are included here
<h:panelGroup id="textPanel" >
<h:form id="main" prependId="false">
<h:outputText value="WorkFlow ID:" />
<h:selectOneMenu id="workFlows" value="#{workFlowSelectionController.selectedWorkFlowId}" >
<p:ajax event="change" listener="#{workFlowSelectionController.dropDownChange}" update="textPanel"/>
<f:selectItems value="#{workFlowSelectionController.allActiveworkFlows}"/>
</h:selectOneMenu>
<p:inputText value="#{workFlowSelectionController.texField}" rendered="#{workFlowSelectionController.textfieldVisibility}"/>
<p:commandButton ajax="false" value="Next" action="#{workFlowSelectionController.addWorkFlowselectionDetails}"/>
</h:form>
</h:panelGroup>
this is my managed bean
#ManagedBean
#RequestScoped
public class WorkFlowSelectionController {
private boolean textfieldVisibility = false;
private String texField;
public void dropDownChange() {
logger.info("WorkFlowSelectionController.dropDownChange() entered");
if (selectedWorkFlowId != null) {
if (selectedWorkFlowId.equals("-1")) {
textfieldVisibility = true;
operationListStatus = false;
} else {
textfieldVisibility = false;
operationListStatus = true;
}
} else {
textfieldVisibility = false;
operationListStatus = true;
}
public void addWorkFlowselectionDetails() throws CloneNotSupportedException {
System.out.println("Selected Value of Text Field is" + texField);
}
public String getTexField() {
return texField;
}
public void setTexField(String texField) {
this.texField = texField;
}
}
i haven't included the dropdown code of the backing bean. i just need an idea of what i am doing wrong here if i remove the rendered attribute of the textfield it works fine.
thank you
Put the bean in the view scope instead of request scope. A request scoped is recreated on every single HTTP request. The boolean property will default to false again whenever you submit the form, so the submitted value won't be processed then.
#ManagedBean
#ViewScoped
public class WorkFlowSelectionController {
//
}
A view scoped bean will live as long as you're (ajax-) interacting with the same view by returning null or void from action(listener) methods.

Resources