How to pass a parameter to JSF dataTable's value attribute - jsf

In a JSF 2.1 application, I need to build a JSF dataTable (using PrimeFaces) that shows only the db records belonging to the logged in user.
So, I need to pass the username to the bean associated to the dataTable's value attribute:
value="#{tableBuilder.records}"
Here is the table bean:
#ManagedBean
#ViewScoped
public class TableBuilder {
private List<MyRecord> records;
private String username;
// getters and setters
}
It's useful to know that the application consists of a single web page, with container-managed authentication implemented through LoginBean, a SessionScoped ManagedBean. This implies the additional effort of notifying to TableBuilder when the user logs in.
The only way I am thinking of is to inject the LoginBean into the TableBuilder through #ManagedProperty annotation, and checking on every request of getRecords if the username property of LoginBean has changed.
Maybe there are better ways?

If your environment supports EL 2.2 (your question history confirms Java EE 6), then "just do it":
<h:dataTable value="#{bean.getModel(user)}">
with
public List<Item> getModel(User user) {
// ...
}
Whether it's the right way or there are better ways, I'll leave in the middle. Keep in mind that a getter is invoked as many times as EL evaluates the value expression.

Related

Getting application scoped bean in JSF 2.2 custom component

I am developing a new JSF 2.2 application.
I have an eagerly created, application scope managed bean that loads up some configuration data from an external file, at startup, and stores it as state.
I have a FacesComponent and FacesRenderer that I have working statically.
I would like to be able to get the configuration data stored in the managed bean into the FacesComponent. Is there a standard way to do this.
As far as I am aware, the managed bean cannot be injected into the component - is that correct?
I can try to get data into the custom component using attributes and el in the .xhtml file that uses the custom component e.g.
<my:customComponent data="#{managedBean.loadedData}"/>
but this seems like a really backwards way to do things and actually exposes internal implementation of the component to the component user.
Please let me know if there is another way, or if you need any more information.
Update: #BalsusC I have tried what you suggested
I have a loader that puts the loaded data into a holder object
#Named
#ApplicationScoped
public class Loader implements Serializable {
#Inject
private Holder holder
#PostConstruct
public void init() {
// Load data into the holder here
}
}
The holder is another application scoped bean
#Named
#ApplicationScope
public class Holder {...}
When loading the data the Holder instance is injected correctly into the Loader.
However when I they the following
#Named //Makes no difference if this is here or not
#FacesComponent(value="family", createTag=true, namespace="...namespace...", tagName="tag")
public class Component extends UIComponentBase {
#Inject
public class Holder holder;
#Override
public void encodeBegin(FacesContext context) {
holder.getData();
}
}
when the component comes to render, the holder is not injected and I get a null pointer exception. I have tried to do this with our without the #Named annotation with the same result.
Am I doing something wrong? Can you please advise.
Application scoped JSF managed beans are internally stored in the application map with the managed bean name as key.
So, the below inside any of UIComponent methods should do:
ManagedBean managedBean = (ManagedBean) getFacesContext().getExternalContext()
.getApplicationMap().get("managedBean");
// ...
This only makes the component tight coupled to the managed bean. I.e. the component can't exist without the managed bean. This should be clearly documented if the component is intented to be reusable in other webapps. Another option is to tie the managed bean exclusively to the component (perhaps in form of a composite component) and use another application scoped bean for "unrelated" application data.

How does EL #{bean.id} call managed bean method bean.getId()

I do not really understand how getter and setter work althougth it is a basic concept. I have the following code, how is the attribute id sent to Managed Bean? Is it captured by getter method?
My facelet
<p:inputText id="id" value="#{bean.id}">
My managed bean
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
The call of getter and setter methods by #{} expressions is not part of JSF but Expression Language (most known as EL). JSF takes advantage of EL to bind the data of the HTML components to the fields of a bean through proper getters and setters. This is:
If the bean exists, Expression Language will execute the proper getter of the registered bean in the proper scope.
If client performs a form submission or an ajax request, then the components that are sent to the server (usually all the components in the <h:form>, in case of ajax requests you can state which components to send to the server) will contain a new value, and this value will be set to the field with the proper setter method.
For example, you have a SayHelloBean which belongs to request scope:
#RequestScoped
#ManagedBean
public class LoginBean {
private String name;
//proper getter
public String getName() {
return this.name;
}
//proper setter
public void setName(String name) {
this.name = name;
}
}
And these 2 facelets pages (since it's an example I avoid declaring <html>, <h:head>, <h:body> and other elements, just focusing on the relevant code)
Page1.xhtml:
<h:form>
Please tell me your name
<h:inputText value="#{loginBean.name}" />
<h:commandButton action="page2" />
</h:form>
Page2.xhtml:
Hello #{loginBean.name}
This is what happens behind the scenes:
When Page1.xhtml is loaded, a new instance of LoginBean, which we may call loginBean, will be created by JSF and registered into JSP request scope. Since the value of <h:inputText /> is bound to LoginBean#name (which is read as the field name of LoginBean class), then EL will display the value of loginBean#name (which is read as the field name of instance loginBean), and since that is not initialized, EL will display null, as an empty string.
When you submit the form of Page1.xhtml, since LoginBean is #RequestScoped then JSF will create a new instance of LoginBean, which we may call it loginBean2 (adding 2 in the end because this instance is totally different from the loginBean previously created) and will register it in JSP request scope. Since the value of <h:inputText /> is bound to LoginBean#name, JSF will validate and set the data by calling the proper setter. This will make loginBean2#name have the value of the <input type="text"> that was rendered by <h:inputText/>.
At last, JSF will make sure to navigate to Page2.xhtml through forward, where when processing it, it will find #{loginBean.name} and EL will check for the value of loginBean2#name and replace it.
The steps explained here are a very small explanation (and with lot of elements not explained) of the JSF lifecycle and how JSF uses getters and setters.
More info:
How to pass parameter to jsp:include via c:set? What are the scopes of the variables in JSP?
How to choose the right bean scope?
The Lifecycle of a JavaServer Faces Application
Differences between Forward and Redirect
Additional note: since you're learning JSF, avoid putting any business logic code in getters/setters. This is greatly explained here: Why JSF calls getters multiple times
Whenever you use something like
#{someBean.someField}
the EL looks for a someBean.getSomeField() or someBean.setSomeField(...) method, depending on whether you're reading that field or writing in it (which can easily be inferred from the context). JSF never accesses a field directly (i.e without making use of its getter or setter). Try deleting the getter and setter of a given field and you'll see it won't work.

CDI (Weld) + DeltaSpike + Converstation

I'm currently trying to get CDI (Weld to be more precise) to work with JSF 2 and a long running conversation. I'm starting the conversation in #ConversationScoped my backing bean. I'm also creating a #ConversationScoped entity manager. Sadly the entity I'm editing in my backing bean always get's a LazyInitializationException when JSF is trying to write a #ManyToMany mapped field. It seems that the entity get's detached from the entitymanager. In my EntityManagerProducer (see below) the method createEntityManager() is not called bevore the LazyInitializationException is thrown, so I'd assume that the entitymanager is actually session scoped. But from what I understand my code does not generate an extended persistence context (since I could not figure how to do that programmatically).
As far as I know DeltaSpike does not yet offer anything for dealing with long running conversations in JSF. Can anybody suggest a method on how to implement long running conversations with CDI using Weld + DeltaSpike (preferably on a Tomcat with weld-servlet)?
#ApplicationScoped
public class EntityManagerProducer {
#Inject
#PersistenceUnitName("myUnit")
private EntityManagerFactory emf;
#Produces
#ConversationScoped
public EntityManager createEntityManager() {
return emf.createEntityManager();
}
public void closeEm(#Disposes EntityManager em) {
em.close();
}
}
The lazy initialization exception actually does not come from extended persistence contexts or CDI or DeltaSpike. It's simply the same behaviour as described here: selectManyCheckbox LazyInitializationException on process validation
JSF 2 clones the PersistentBag of my entity causing it to get detached. I also worked around by sprinkling lot's of
<f:attribute name="collectionType" value="java.util.ArrayList" />
throughout my JSF code.
Having the EntityManager in a conversation scope is only one side of the equation, what scope are your entities in ?
In order to avoid detaching the entities they should also be conversation scoped.
As a side note :
I found that instead of dealing with entities in a long running scope , it is much easier to reattach them in each request scope.

Injecting one view scoped bean in another view scoped bean causes it to be recreated

I need to use some data saved in a view scoped bean in an other view scoped bean.
#ManagedBean
#ViewScoped
public class Attivita implements Serializable {
//
}
and
#ManagedBean
#ViewScoped
public class Nota implements Serializable {
#ManagedProperty("#{attivita}")
private Attivita attivita;
// Getter and setter.
}
Now, maybe my theory about it is still quite poor, I have noticed that when #{attivita} is injected, the Attivita constructor is invoked and thus creating another instance. Is it the right behaviour? What about if I want to reference the same instance and not create a new one?
This will happen if you're navigating from one to the other view on a postback. A view scoped bean is not tied to a request, but to a view. So when you navigate to a new view, it will get a brand new instance of the view scoped bean. It won't reuse the same bean instance which is associated with a previous view.
I understand that the attivita bean is created on the initial view and reused on postback. I understand that nota bean is associated with the new view where you're navigating to. When injecting attivita in it, it will simply get a new and distinct instance even though there's another instance in the very same request. This is all expected (and admittedly a bit unintuitive) behaviour.
There is no standard JSF solution for this. CDI solves this with #ConversationScoped (the bean lives as long as you explicitly tell it to live) and the CDI extension MyFaces CODI goes a bit further with #ViewAccessScoped (the bean lives as long as the navigated view references it).
You could however workaround this by storing the bean as an attribute in the request scope.
#ManagedBean
#ViewScoped
public class Attivita implements Serializable {
public String submit() {
FacesContext.getCurrentInstance().getExternalContext()
.getRequestMap().put("attivita", this);
return "nota";
}
}
and
#ManagedBean
#ViewScoped
public class Nota implements Serializable {
private Attivita attivita;
#PostConstruct
public void init() {
attivita = (Attivita) FacesContext.getCurrentInstance().getExternalContext()
.getRequestMap().get("attivita");
}
}
Note that this is rather hacky. There may be better solutions depending on the concrete functional requirement. Also note that you should in the nota view reference the desired Attivita bean instance as #{nota.attivita} and not as #{attivita}, because it would give you a new and different instance, for the reasons already explained before.
Your attivita bean is #ViewScoped and that doesn't guarantee that your instance will be hold in session. You need a #SessionScoped bean. However, if you need attivita for some reason to be #ViewScoped, then you can pass params through them in other ways, e.g. using viewParam or using other #SessionScoped bean between them.
Page Params
http://mkblog.exadel.com/2010/07/learning-jsf2-page-params-and-page-actions/
JSF 2 Managed Bean Scopes
http://balusc.blogspot.com.es/2011/09/communication-in-jsf-20.html#ManagedBeanScopes

Reset JSF Backing Bean(View or Session Scope)

I want to reset by JSF backing bean when some method is invoked. Assume that there is a command button, someone press it and after succesfull transaction, my View or Session scope JSF bean should be reseted. Is there a way to do that?
Thank
I found the solution for View scope.
public static void removeViewScopedBean(String beanName)
{
FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(beanName);
}
A view scoped bean will be recreated when you return non-null or non-void from the action method, even if it would go back to the same view. So, just return a String from the action method, even if it's just an empty string:
public String submit() {
// ...
return "";
}
To make it complete, you could consider sending a redirect by appending the ?faces-redirect=true query string to the returned outcome.
public String submit() {
// ...
return "viewId?faces-redirect=true";
}
A session scoped bean is in first place the wrong scope for whatever you're currently trying to achieve. The bean in question should have been be a view scoped one. Ignoring that, you could just recreate the model in the action method, or very maybe invalidate the session altogether (which would only also destroy all other view and session scoped beans, not sure if that is what you're after though).
just clear all views:
FacesContext.getCurrentInstance().getViewRoot().getViewMap().clear();
and remember to implements Serializable in all views
You could also refresh the page from javascript, so the ViewScoped Bean will be reseted, for example in a primefaces commandButton:
<p:commandButton value="Button" action="#{bean.someAction()}" oncomplete="location.reload()"/>
I solve the problem with code like this:
((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().removeAttribute("bean name");
By this way I enter to session scoped bean and reset it without the data that was there before

Resources