Applying request values to entity bean loaded with id from inputHidden before other fields
-
17-06-2021 - |
Question
I have a facelet template with:
<f:metadata>
<o:viewParam name="id" value="#{homeBean.id}" />
</f:metadata>
<h:form>
<h:inputHidden value="#{homeBean.id}" />
<h:inputText value="#{homeBean.user.firstName}" />
<h:commandButton value="Submit" action="#{homeBean.onSave()}" />
</h:form>
and a request scoped bean with:
@Named
@RequestScoped
public class HomeBean {
private Integer id;
private User user;
public void setId(Integer id) {
System.out.println("setId called");
user = // code for loading User entity bean with supplied id
}
// other accessors for id and user
}
Initial page load works well, entity is loaded and displayed in a form, inputHidden is set to entity id. Problem is that submit throws:
javax.el.PropertyNotFoundException - Target unreachable, base expression '. user' resolved to null
probably because getUser is called before setId. How can I solve this? I really would like to have a request scoped bean, I know that this can be easily solved with at least viewaccess scoped bean.
EDIT: Now i noticed that exception is thrown in Process Validations phase, I initially thought that exception is thrown in Update Model Values phase. I changed "private User" to "private User user = new User()" and now it's OK, but it feels little weird.
Regards, Pavel
Solution
The OmniFaces <o:viewParam>
sets the request parameter only in the initial request and not in postbacks. This is intented to be used with @ViewScoped
beans so that the request parameter isn't unnecessarily been validated, converted and updated on every single postback (because it's already still present in a view scoped bean). The API documentation and the showcase example also explicitly mentions that it should be used with view scoped beans.
You've there however a request scoped bean which get trashed and recreated on every single request, also on postbacks to the same view. So the user
property disappears and falls back to default null
on every subsequent postback request.
There are basically 2 ways to fix it:
Replace
<o:viewParam>
by<f:viewParam>
. It will call the setter on every request, also on postbacks.Replace
@Named @RequestScoped
by@ManagedBean @ViewScoped
, this way the bean will live as long as you're interacting with the same view. Or if you insist in using CDI, use@Named @ConversationScoped
instead, but you have to manage the begin and end of the conversation yourself.