#ViewScoped, f:setPropertyActionListener only works once - jsf

I have 2 managedBeans: the first one (RequestScoped) sends a parameter to the second one (ViewScoped) through a h:commandLink. Both managedBeans are in the same page, but I use them in different tabs from a rich:TabPanel:
#ManagedBean
#RequestScoped
public class TheRequestScopedManagedBean {
private String number
...
#ManagedBean
#ViewScoped
public class TheViewScopedManagedBean {
private String number;
...
And here's the view, wich uses a rich:dataTable:
(The action method is only for showing the second tab from a rich:tabPanel).
// ... another dataTable's columns
<rich:column>
<f:facet name="header">Number</f:facet>
<b>
<a4j:commandLink value="#{theRequestScopedManagedBean.number}"
render="someRichPanel" action="#{anotherBean.showSecondTab}" immediate="true">
<f:setPropertyActionListener target="#{theViewScopedManagedBean.number}" value="#{theRequestScopedManagedBean.number}" />
</a4j:commandLink>
</b>
</rich:column>
The problem here is that theViewScopedBean shows the value only the first time, and when I try to pass it again, it shows its default value (null).
I've seen several questions on this website. But I really don't know what to do in this case.

action="#{anotherBean.showSecondTab}" is going to result in a navigation case firing. This will in turn result in the views and request scoped beans being destroyed and recreated (as is the expected behaviour for a view scoped bean).
If you're using EL2.2, you could easily just pass the value directly into a method in the view scoped bean. Let's assume your viewscoped bean has a method takeValue, you can pass the parameter directly into the method as in:
<a4j:commandLink value="#{theRequestScopedManagedBean.number}" render="someRichPanel" action="#{theViewScopedBean.takeValue(theRequestScopedManagedBean.number)}" immediate="true"/>
There are still cleaner ways to transmit data between pages.
Communication in JSF2.0

Related

Why is this ViewScoped managed bean not working and how can I make it work?

This is the list page I have:
<h:dataTable value="#{actorSearchBacking.all}" var="actor">
<h:column>
<f:facet name="header">
First Name
</f:facet>
#{actor.firstname}
</h:column>
<h:column>
<f:facet name="header">
Last Name
</f:facet>
#{actor.lastname}
</h:column>
<h:column>
<h:form>
<h:commandButton value="Update Actor" action="pocdetail">
<f:setPropertyActionListener target="#{actorFormBacking.stupidActor}" value="#{actor}"/>
</h:commandButton>
</h:form>
</h:column>
</h:dataTable>
which looks something like this in my local environment:
This is pocdetail.xhtml which is the action of Update Actor button:
<h:body>
<h:form id="updateActorForm"
prependId="false">
<h:inputText id="firstname" value="#{actorFormBacking.stupidActor.firstname}"/>
<h:inputText id="lastname" value="#{actorFormBacking.stupidActor.lastname}"/>
<h:commandButton id="updateActorButton"
value="Update Actor!"
action="#{actorFormBacking.updateActor()}"/>
</h:form>
</h:body>
And finally ActorFormBacking is as follows:
#ManagedBean
#ViewScoped
public class ActorFormBacking implements Serializable {
private Actor stupidActor;
public Actor getStupidActor() {
return stupidActor;
}
public void setStupidActor(Actor stupidActor) {
this.stupidActor = stupidActor;
}
}
When I debug the application, I see that setStupidActor is called and property stupidActor is set, but then when getter is called, it is again null.
Since this is a ViewScoped bean, I am expecting the stupidActor not to be null and I expect to see the pocdetail.xhtml page to be filled with values, but all I see is empty input texts since stupidActor is null.
What is it that I am missing? Why is the ViewScoped bean created again and the property is null?
Btw, I am using the annotations from the packages:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
It appears that you're navigating from one view to another view. In other words, you destroy the current view and create a new view. Logically, the view scope will also get destroyed and newly created, including all view scoped managed beans. That the view scoped managed bean happens to be referenced by both views doesn't change this behavior.
A view scoped bean lives as long as the view itself. Like as that a request scoped bean lives as long as the request itself and so on. In order to have a better understanding of the lifetime of various scopes in JSF (and CDI), head to this Q&A: How to choose the right bean scope?
The functional requirement is however understood. You want separate master-detail pages and pass the selected item from the master page to the detail page for editing. There are several ways to achieve this:
The canonical way is to just use a bookmarkable GET link instead of an unbookmarkable POST link. Replace the below piece
<h:form>
<h:commandButton value="Update Actor" action="pocdetail">
<f:setPropertyActionListener target="#{actorFormBacking.stupidActor}" value="#{actor}"/>
</h:commandButton>
</h:form>
by this
<h:link value="Update Actor" outcome="pocdetail">
<f:param name="stupidActor" value="#{actor.id}" />
</h:link>
and in the detail page, obtain the Actor by its identifier which is passed-in as query string parameter. This is fleshed out in detail in this Q&A: Creating master-detail pages for entities, how to link them and which bean scope to choose. A #FacesConverter(forClass) is very useful here.
In case you want to stick to POST for some reason, then your best bet is storing it in the request scope.
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("stupidActor", stupidActor);
and retrieve it in the #PostConstruct of the very same bean
stupidActor = (Actor) FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("stupidActor");
If you happen to use CDI, or are open to (which I strongly recommend though, JSF managed beans are already deprecated in JSF 2.3.0-m06, see also Backing beans (#ManagedBean) or CDI Beans (#Named)?), then consider using MyFaces CODI's #ViewAccessScoped. Beans with this scope will live as long as all postbacked views explicitly reference the bean. Once you navigate out with a GET, or when the navigated view doesn't anywhere reference that bean, then it will get destroyed.
#Named
#ViewAccessScoped
public class ActorFormBacking implements Serializable {}
Merge the both views into a single view with conditionally rendered master-detail sections. You can find a kickoff example in this Q&A: Recommended JSF 2.0 CRUD frameworks. Or if you happen to use PrimeFaces, How to show details of current row from p:dataTable in a p:dialog and update after save.

Setting Managed Bean attribute's value from JSF page and using it in bean's methods

I have a ViewScoped Managed Bean. In my .xhtml page I want to set bean's attribute's value and use it in methods in the same bean.
I managed to set the value from jsf page, but when i want to use it in some method the value of an attribute is not the value i have set before.
Description (xhtml):
In this form there is a command link which sets the value of an attribute. And it is working fine. Also, as command link is clicked, second form is being showed.
<h:form>
<h:commandLink value="Set" >
<f:setPropertyActionListener target="#{bean.attribute}" value="true" />
<f:ajax execute="#this" />
</h:commandLink>
</h:form>
This form executes method that uses attribute's value set before, but the value is not true, its false.
<h:form>
<h:commandButton id="submit" value="Execute" action="#{bean.execute}" />
</h:form>
Bean:
public void execute(){
if(isAttribute())
---do something---
}
The question is: Why execute() is not reading attribute's value right?
When I use one form, it's working fine. But I need them to be in separated forms.
The scope of your bean is incorrect. ViewScoped means that the minute the view is changed, the bean is discarded and re-created for the next view. So, in your case, the original data you had for the first view is lost.
I'm going to refer you to BalusC's blog:
http://balusc.blogspot.co.uk/2010/06/benefits-and-pitfalls-of-viewscoped.html
which states:
A #ViewScoped bean will live as long as you're submitting the form to the same view again and again. In other words, as long as when the action method(s) returns null or even void, the bean will be there in the next request. Once you navigate to a different view, then the bean will be trashed
I can't determine of you stay on the same page with both requests. If you do, viewScope should work even in two different forms. If you are navigating from 1 view to another, another viewScope will be created and you will loose the current one.
You could set the value in the sessionScope with java or by annotating the backingNean. But then everything in your backingBean becomes sessionScoped and that might not be needed.
You could also use a spring-like flow scope.
Example to do it with java:
public void callThisAfterFirstClick() {
Faces.setSessionAttribute(attribute, true)
}
public void callThisAfterSecondClick() {
Faces.getSessionAttribute(attribute);
}

How to use component binding in JSF right ? (request-scoped component in session scoped bean)

Mojara 2.1.21
I've updated my question based on comments. I have two situation where a component is bound to server session bean. (Additional links with information: Binding attribute causes duplicate component ID found in the view and https://stackoverflow.com/a/12512672/2692917)
Version 1:
single.xhtml:
<h:outputText value=... binding="#{mysessionbean.out}" />
java:
#SessionScoped #Named public class Mysessionbean {
UIOutput out;
//getter and setter ....
}
Version 2:
template.xhtml:
<h:outputText value=... binding="#{mysessionbean.out}"
view1.xhtml:
<ui:composition template="template.xhtml" />
view2.xhtml:
<ui:composition template="template.xhtml" />
java:
#SessionScoped #Named public class Mysessionbean {
UIOutput out;
//getter and setter ....
}
Version 1 is ok. (At least I've not encounter any errors so far). But in version 2 the duplicate id error is occured if I navigate from one page to another. Why does it happen ?
Is it safe to use (request-scoped) component (in version 1) with session scoped binding ?
Are there another use cases to consider ?
Edit:
Functional requirement 1:
I want to use Primefaces datatable in a view. I need some info from this datatable. (Such as selected row or row index). So binding the datatable helps me to retrieve this info.
Functional requirement 2:
Components binding in composite components. They will be bound to session scoped bean. (And used mainly on one page, but what if I used it on another page ?
Requirements 3
The situation as in "Version 2". Template with primefaces menu and session scoped binding. For this I've used the EL-Binding.
In JSF 2.x, unless you want to manipulate components programmatically (which is at its own also rather fishy), there is no sensible real world use case to bind components to a backing bean. For sure not if they are further not been used in the backing bean itself, or if it are solely their attributes which are been flattened out.
As to the functional requirement of getting the current row of the data table, there are much better ways listed here, How can I pass selected row to commandLink inside dataTable?, for example if your environment supports EL 2.2:
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<h:commandLink value="Foo" action="#{bean.foo(item)}" />
The two last requirements are totally unclear. At least, if you're doing something like:
<x:someComponent binding="#{bean.someComponent}" />
with in bean
someComponent.setSomeAttribute(someAttribute);
someComponent.setOtherAttribute(otherAttribute);
then you should instead be doing
<x:someComponent someAttribute="#{bean.someAttribute}" otherAttribute="#{bean.otherAttribute}" />
Or, if you intend to be able to use the component somewhere else in the view like so
<h:inputText ... required="#{not empty param[bean.save.clientId]}" />
...
<h:commandButton binding="#{bean.save}" ... />
and the instance is further nowhere been used in the bean, then just get rid of the unnecessary property altogether:
<h:inputText ... required="#{not empty param[save.clientId]}" />
...
<h:commandButton binding="#{save}" ... />
If there is really, really no way for some unclear reason, then split all request scoped properties of the session scoped bean out into a separate request scoped bean which you in turn bind to form actions. The session scoped one can just be injected as a #ManagedProperty of the request scoped one.
See also:
Binding attribute causes duplicate component ID found in the view
How does the 'binding' attribute work in JSF? When and how should it be used?
We ran into a similar problem and I just want to share our solution:
Problem:
In a view there was a (extended largely customized) datatable.
<x:dataTable binding="#{bean.someSomeDataTable}" />
After navigating to another page and back we wanted the datatable to have the exact same state. Previously we solved that by binding the datatable to to backing bean. This worked fine with JSPs. With Facelets we could not do that (Duplicate ID errors). So we used the binding, but only saved/restored the state of the datatable component.
public HtmlDataTable getSomeDataTable()
{
HtmlDataTable htmlDataTable = new HtmlDataTable();
if (tableState != null)
htmlDataTable.restoreState(FacesContext.getCurrentInstance(), tableState);
return htmlDataTable;
}
public void setSomeDataTable(HtmlDataTable table)
{
tableState = table.saveState(FacesContext.getCurrentInstance());
}

JSF: My dynamically added elements don't react

This questions seems to be so simple, yet I didn't find the mistake.
Ok, here we go: I have a form with a List of IPs (whatever),
IP n: [INPUT-FIELD] [Button "Add"]
IP n+1: [INPUT-FIELD] [Button "Add"]
These IPs exist right from the start.
They are initialized in the Constructor of the Managed Bean.
When I click on the "Add" Button, the current IP is validated. After that
I add another entry to my java.util.List and another line in the
JSF form appears:
IP n+2: [INPUT-Field] [Button "Add"]
and so on.
Adding a line (with a new IP) and validating the current IP
work perfect for the existing values IP n and IP n+1,
but validating and adding does NOT work with the dynamically
added IP n+2. Neither validating the dynamically generated input field
works nor adding another line with the newly created "Add"-Button.
What am I doing wrong?
<h:form id="frmSpecial" prependId="false">
<h:panelGroup id="tblDestFw">
<c:forEach items="#{bean.fwdest}" var="fwdest">
IP: <h:inputText value="#{fwdest.ip}" validator="#{bean.validateIP}"/>
<h:commandButton action="#{bean.addFwDest()}"
value=">>" title="Add another line">
<f:ajax execute="#this" render="tblDestFw"/>
</h:commandButton>
<br/>
</c:forEach>
</h:panelGroup>
</h:form>
#ManagedBean(name = "bean")
#ViewScoped
public class EnterNewSystemFW implements Serializable {
List<FirewallDest> fwdest;
public EnterNewSystemFW() {
fwdest.add(new FirewallDest("N"));
fwdest.add(new FirewallDest("N+1"));
}
public void addFwDest() {
fwdest.add(new FirewallDest());
}
}
Thanks.
Bernd
As to adding the new line, your <c:forEach> references a view scoped bean property. Due to JSF issue 1492, this bean is reconstructed on every request.
Replace it by a normal JSF component like <ui:repeat> or <h:dataTable> and it'll work as expected.
<ui:repeat value="#{bean.fwdest}" var="fwdest">
...
</ui:repeat>
As to performing validation, your <f:ajax execute="#this"> processes only the current component (the command button), not the inputs. Use <f:ajax execute="#form"> instead to process the entire form.
See also:
JSTL in JSF2 Facelets... makes sense?

h:commandButton not working inside h:dataTable

I am trying to execute an action through commandButton inside a dataTable, but the action is not invoked when the commandButton is placed inside the datatable as shown below
<h:form>
<h:dataTable value="#{bean.list}" var="item">
<h:column>
<h:commandButton value="submit" action="#{bean.submit}" />
</h:column>
</h:dataTable>
</h:form>
When I move the commandButton out of dataTable, the action is successfully executed. What is the problem when commandButton is inside datatable? The commandLink has the same problem.
This problem can happen when the list behind #{bean.list} is not exactly the same during the HTTP request of processing the form submit as it was during the request of displaying the form. JSF will namely re-iterate over the list to locate the button pressed and invoke its action.
If the bean is request scoped and the list is not repopulated during bean's (post)construction, or the list's population depends on a request scoped variable which was lost during the form submit, then JSF will retrieve an empty or a completely different list while processing the form submit and thus won't be able to locate the button pressed and won't invoke any action.
The best fix is to put the bean in the view scope and ensuring that you're loading the data model the proper way.
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
private List<Item> list;
#EJB
private ItemService service;
#PostConstruct
public void init() {
list = service.list();
}
// ...
}
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 4
Benefits and pitfalls of #ViewScoped

Resources