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

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());
}

Related

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);
}

#ViewScoped, f:setPropertyActionListener only works once

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

JSF 2 enum property lost after bean validation

Using JSF2 (MyFaces 2.1) and CDI (Weld) I use a selectOneMenu filled with an enum to conditionally render a second selectOneMenu (doublemenu in the code). There are several other fields in the form which are validated using bean validation.
The problem:
If there are validation errors after clicking on the commandButton and error messages are shown, the doublemenu will not reappear. It appears that bean.type =='double' is not true. But why? The selectOneMenu itself shows the option double as the selected item.
#javax.inject.Named
#javax.enterprise.context.RequestScoped
public class Bean {
private enum MyEnum {
Single, Double
}
private MyEnum type;
public MyEnum getType() {
return type;
}
public void setType(MyEnum type) {
this.type = type;
}
public MyEnum [] getTypes() {
return MyEnum.values();
}
<h:form>
<h:selectOneMenu value="#{bean.type}">
<f:selectItems value="#{bean.types}" />
<f:ajax render="doublemenu" />
</h:selectOneMenu>
<h:panelGroup id="doublemenu">
<h:panelGroup rendered="#{bean.type == 'double'}">
<h:selectOneMenu ...>
</h:selectOneMenu>
</h:panelGroup>
</h:panelGroup>
<h:inputText id="VALIDATED"/>
<h:commandButtonaction="#{bean.save}"
</h:form>
BTW: bean.save is not executed
It's because your bean is request scoped. Request scoped beans are garbaged by end of every request and newly created on begin of every request. This also applies to individual ajax requests on the same view.
The rendered attribute is also evaluated during collecting submitted values (the apply request values phase). However, at the point the submitted values are to be collected, the #{bean.type} is obviously not been set yet (it has still to be set based on the collected submitted value during update model values phase). As your bean is request scoped, it would return the default value, not the submitted value from a previous request.
There are basically 2 ways to fix this.
Put the bean in a bit broader scope. JSF offers the #ViewScoped for exactly this purpose. This is in the current JSF 2.1 version only not compatible with CDI. If switching to JSF #ManagedBean is not an option, then you'd need MyFaces CODI to bridge the JSF #ViewScoped transparently to CDI, or to wait for JSF 2.2 to get a CDI compatible #ViewScoped out the box.
Check a request parameter instead of a bean property.
<h:selectOneMenu binding="#{type}" ...>
...
</h:selectOneMenu>
<h:panelGroup rendered="#{param[type.clientId] == 'Double'}">
Please note that this all is completely unrelated to enums. You'd have had exactly the same problem when using e.g. a String. Also please note that I fixed a typo in your code example, an enum value of Double is definitely not the same as double.
See also:
How to choose the right bean scope?
commandButton/commandLink/ajax action/listener method not invoked or input value not updated

Primefaces commandButton: f:attribute does not work

Project uses Spring Webflow and JSF (PrimeFaces). I have a p:commandButton with f:attribute
<p:commandButton disabled="#{editGroupMode=='edit'}" action="edit_article_group" actionListener="#{articleGroupManager.setSelectedRow}" ajax="false" value="Edit">
<f:attribute name="selectedIndex" value="${rowIndex}" />
</p:commandButton>
Backend code (Spring injected bean):
#Service("articleGroupManager")
public class ArticleGroupManagerImpl implements ArticleGroupManager{
public void setSelectedRow(ActionEvent event) {
String selectedIndex = (String)event.getComponent().getAttributes().get("selectedIndex");
if (selectedIndex == null) {
return;
}
}
}
The attribute "selectedIndex" is always null. Anybody knows what happened here? Thank you.
The variable name "rowIndex" suggests that you've declared this inside an iterating component, such as <p:dataTable>.
This is then indeed not going to work. There's physically only one JSF component in the component tree which is reused multiple times during generating HTML output. The <f:attribute> is evaluated at the moment when the component is created (which happens only once, long before iteration!), not when the component generates HTML based on the currently iterated row. It would indeed always be null.
There are several ways to achieve your concrete functional requirement anyway. The most sane approach would be to just pass it as method argument:
<p:commandButton value="Edit"
action="edit_article_group"
actionListener="#{articleGroupManager.setSelectedRow(rowIndex)}"
ajax="false" disabled="#{editGroupMode=='edit'}" />
with
public void setSelectedRow(Integer rowIndex) {
// ...
}
See also:
JSTL in JSF2 Facelets... makes sense?
How can I pass selected row to commandLink inside dataTable?
Unrelated to the concrete problem, I'd in this particular case have used just a GET link with a request parameter to make the request idempotent (bookmarkable, re-executable without impact in server side, searchbot-crawlable, etc). See also Communication in JSF 2.0 - Processing GET request parameters.

JSF component binding - some confusion

From web pages like this one,
http://www.jsftutorials.net/components/step5.html
I understand that the binding attribute in JSF tag/view component is to bind the view component to a Java instance of the UI component in the backing bean.
E.g., that's what is done in the following code:
<h:inputText value="#{ myBean.someProperty}" binding="#{ myBean.somePropertyInputText}"/>
But sometimes I see code like this:
<h:commandButton id="t1" binding="#{foo}" value="Hello, World!" onclick="alert('I am #{id:cid(foo)}'); return false;" />
where id:cid is a taglib function which is defined as follow:
public static String cid(UIComponent component) {
FacesContext context = FacesContext.getCurrentInstance();
return component.getClientId(context);
}
In the above code, binding="#{foo}" does not bind to "a Java instance of the UI component in the backing bean".
So what is the meaning of expressions such as binding="#{foo}" ?
It just binds the component to the current Facelet scope. This is particularly useful if you don't need it in the backing bean at all. This saves your backing bean code from useless properties which aren't been used in any of the other methods at all. Note that it also works that way in JSF 1.2. Not sure about JSF 1.0/1.1 though as it uses a different and JSF-proprietary EL API.
See also:
JSF component binding without bean property

Resources