Tracking previous page in JSF - jsf

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>

Related

Implementing a commandButton that returns to the previous page

What I want to do is implement a JSF page that will return to the prior page once it is done. This is with #RequestScoped beans.
The original page called viewDocument.xhtml has this:
<f:metadata>
<f:viewParam name="ID" value="#{viewDocument.id}" />
</f:metadata>
(bunch of stuff)
<h:link outcome="editThingy" value="Edit Thingy">
<f:param name="ID" value="#{viewDocument.doc.id}" />
<f:param name="Return" value="viewDocument" />
</h:link>
The target page editThingy.xhtml has its own backing bean, and looks like this:
<f:metadata>
<f:viewParam name="ID" value="#{editThingy.id}" />
<f:viewParam name="Return" value="#{editThingy.navReturn}" />
</f:metadata>
<h:form>
(bunch of stuff)
<p:commandButton value="Save" action="#{editThingy.save}" >
<f:param name="ID" value="#{editThingy.id}" />
</p:commandButton>
<p:commandButton value="Cancel" action="#{editThingy.navReturn}" >
<f:param name="ID" value="#{editThingy.id}" />
</p:commandButton>
</h:form>
The idea is that both buttons (cancel or save) would return to the page indicated by the Return request parameter, with the new page getting the ID request parameter so it pulls up the same record that EditThingy worked on.
BZZZT! The p:commandButton does a POST and not a GET, so it can't use f:param. However if I try to use p:button there is no attribute to call the backing bean!
Is there any way to implement what I want?
The <f:param> should work just fine. You only need to retain the Return parameter for subsequent requests as well, exactly as you did for the ID parameter, otherwise it's lost during processing the form submit because your bean is request scoped instead of view scoped.
Thus so,
<p:commandButton value="Save" action="#{editThingy.save}" >
<f:param name="ID" value="#{editThingy.id}" />
<f:param name="Return" value="#{editThingy.navReturn} " />
</p:commandButton>
<p:commandButton value="Cancel" action="#{editThingy.cancel}" >
<f:param name="ID" value="#{editThingy.id}" />
<f:param name="Return" value="#{editThingy.navReturn} " />
</p:commandButton>
with
public String save() {
// ...
return navReturn;
}
public String cancel() {
return navReturn;
}
Update: based on the comments, the functional requirement has now become more clear. The cancel button can also be done as follows, assuming that you don't need to invoke a backing bean action method at all on cancel:
<p:button value="Cancel" outcome="#{editThingy.navReturn}" >
<f:param name="ID" value="#{editThingy.id}" />
</p:button>
The save button can return the following:
return navReturn + "?id=" + id + "&faces-redirect=true";

JSF: what is the "correct" way of doing this?

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" />

Problems with conditionals in JSF

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>

Glassfish complaining about JSF component IDs

I am very new to JSF (v2.0) and I am attempting to learn it at places like netbeans.org and coreservlets.com. I am working on a very simple "add/subtract/multiply/divide" Java webapp and I have run into a problem. When I first started out, the application was enter two numbers and hit a '+' key and they would be automatically added together. Now that I have added more complexity I am having trouble getting the operation to the managed bean. This is what I had when it was just "add":
<h:inputText styleClass="display" id="number01" size="4" maxlength="3" value="#{Calculator.number01}" />
<h:inputText styleClass="display" id="number02" size="4" maxlength="3" value="#{Calculator.number02}" />
<h:commandButton id="add" action="answer" value="+" />
For the "answer" page, I display the answer like this:
<h:outputText value="#{Calculator.answer}" />
I had the proper getters and setters in the Calculator.java managed bean and the operation worked perfectly.
Now I have added the other three operations and I am having trouble visualizing how to get the operation parameter to the bean so that I can switch around it. I tried this:
<h:commandButton id="operation" action="answer" value="+" />
<h:commandButton id="operation" action="answer" value="-" />
<h:commandButton id="operation" action="answer" value="*" />
<h:commandButton id="operation" action="answer" value="/" />
However, Glassfish complained that I have already used "operation" once and I am trying to use it four times here.
Any adivce/tips on how to get multiple operations to the managed bean so that it can preform the desired operation?
Thank you for taking the time to read.
The component id should indeed be unique. This is implicitly required by the HTML specification. You know, all JSF does is just generating the appropriate HTML/CSS/JS code. Give them all a different id or just leave it away, it has no additional value in this specific situation (unless you'd like to hook some CSS/JS on it).
To achieve your functional requirement, you may find f:setPropertyActionListener useful.
<h:commandButton action="answer" value="+">
<f:setPropertyActionListener target="#{calculator.operation}" value="+" />
</h:commandButton>
<h:commandButton action="answer" value="-">
<f:setPropertyActionListener target="#{calculator.operation}" value="-" />
</h:commandButton>
<h:commandButton action="answer" value="*">
<f:setPropertyActionListener target="#{calculator.operation}" value="*" />
</h:commandButton>
<h:commandButton action="answer" value="/">
<f:setPropertyActionListener target="#{calculator.operation}" value="/" />
</h:commandButton>
And have a property operation in your calculator managed bean:
private String operation; // +setter.
You can access it in the getAnswer() method and handle accordingly.
Alternatively, let the buttons each point to a different bean action but which returns all "answer":
<h:commandButton action="#{calculator.add}" value="+" />
<h:commandButton action="#{calculator.substract}" value="-" />
<h:commandButton action="#{calculator.multiply}" value="*" />
<h:commandButton action="#{calculator.divide}" value="/" />
with the following methods in your calculator managed bean:
public String add() {
answer = number1 + number2;
return "answer";
}
public String substract() {
answer = number1 - number2;
return "answer";
}
// etc...
and just let getAnswer() return answer and do nothing else there. That's a more clean separation of responsibilities.

JSF passing in expression value into a commanButton

I am trying to access the value of the expression $result.id and use that value and pass it in the bean action commitBtn. how do I go about doing this.
<c:forEach items="${bean.results}" var="result">
<fieldset>
<legend>
<b>Title:</b>
${result.id}
<c:set var="Id" value="${result.id}" />
<!-- this Id doesn't show as well, why -->
<h:outputText value="#{Id}" binding="#{bean.Id_lbl}" id="iD_lbl" />
<h:commandButton value="Commit" binding="#{bean.commitBtn}"
id="commitBtn" action="#{bean.commitBtn}" />
</legend>
...
Use the <f:param> tag to pass the value to the action. Within the action you will have to get the param value from the requestMap
something like:
<h:commandButton blah blah blah>
<f:param name="resultId" value="${result.id}" />
</h:commandButton>
then in action code:
resultId = FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("resultId");
The f:param as some may suggest does not work in a h:commandButton in JSF 1.x. Use f:setPropertyActionListener instead:
<h:commandButton value="Commit" binding="#{bean.commitBtn}" id="commitBtn" action="#{bean.commitBtn}">
<f:setPropertyActionListener target="#{bean.resultId}" value="#{result.id}" />
</h:commandButton>
Also see this article for more hints.

Resources