This question already has an answer here:
JSF does not populate #Named #RequestScoped bean with submitted input values
(1 answer)
Closed 5 years ago.
I have a bean with a field called 'name', and a JSF form that has an inputText mapped with this field. The initial value of the field is well displayed on the form.
The problem is when I submit the form, the value is not updated with the content of the inputText. In the savePlayer() method below, the value of name is always 'name', not what I typed inside the form input.
The bean :
#Named
#RequestScoped
public class PlayerForm {
#Inject
private PlayerRepository playerRepository;
private String name = "name";
public String savePlayer(){
Player player = new Player();
player.setName(name);
playerRepository.savePlayer(player);
return "saveUserOk";
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
The form :
<h:form>
<h:inputText value="#{playerForm.name}" />
<h:commandButton value="Submit" action="#{playerForm.savePlayer}" />
</h:form>
Thanks very much for any help!
This can happen if you imported #RequestScoped from the package javax.faces.bean (JSF) instead of from javax.enterprise.context (CDI). Every single EL expression #{} would then create a brand new and completely separate instance of the bean. The given form example would then end up in two instances of the bean, one where the name is set and another where the action is invoked.
The javax.faces.bean.RequestScoped annotation can only be used in conjunction with JSF's own #ManagedBean annotation not with CDI's #Named annotation.
Related
I use lombok to omit the getter and setter of a java bean.
Here is a example from the book "Spring in Action 5 edition"
A java bean:
#Data
public class Taco {
#Size(min=1, message="You must choose at least 1 ingredient")
private List<String> ingredients;
}
Controller:
#PostMapping
public String processDesign(#Valid #ModelAttribute("design") Taco design, Errors errors, Model model) {
if (errors.hasErrors()) {
return "design";
}
System.out.println(design.getIngredients());
return "redirect:/orders/current";
}
The rendered view:
<form method="POST">
<input name="ingredients" type="checkbox" value="FLTO">
<span>Flour Tortilla</span><br>
<input name="ingredients" type="checkbox" value="GRBF">
<span>Ground Beef</span><br>
</form>
When I submit the form and no checkbox is checked,the validation does not work,in the controller, errors.hasErrors() is false,and design.getIngredients() is null
Then I change the code in the java bean:
private List<String> ingredients=new ArrayList<>();
validation works,user will get the message:"You must choose at least 1 ingredient"
But my question is : should I instantiate a field even I already used lombok,especially for a reference field?Is there a way use an annotation to do it?
You can define the nullary constructor and initialize the List inside it. Lomboks constructor will be overridden.
#Data
public class Taco {
#Size(min=1, message="You must choose at least 1 ingredient")
private List<String> ingredients;
public Taco() {
ingredients = new ArrayList<>()
}
}
I have a bean like this:
public class QuestionBean
{
private int myQuestionId;
private String myContext;
private String myQuestionType;
private String myQuestionAns;
// setters and getters here
}
Which I put in a list List<UserBean> = new ArrayList<UserBean>;
I want to use this list with the form, consider something like this:
<form:form commandName="questionBean"..
<form:input path="myQuestionId"...
My case is this:
Each question will have the attributes I listed in the bean ( that is why I put the bean in a List )
There is only 1 Form
My question is how can I use the List of bean, if the form is only one and I need the a List of bean to be binded to each question?
I am developing Spring 3 + Struts2 application and I configure my Actions in Spring as follows:
<bean id="patientSearchAPIClass" class="com.axiohelix.nozoki.web.action.api.PatientSearch">
<property name="searchService" ref="searchService"/>
</bean>
But in my Action class I keep fields to store Request parameters ,
public class PatientSearch extends ActionSupport {
public String getPharumoId() {
return pharumoId;
}
public void setPharumoId(String pharumoId) {
this.pharumoId = pharumoId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
private String pharumoId;
..
public String execute(){
searchResults=searchService.searchPatients(pharumoId, name,
birthday,
pharmacyId,
clinic,
doctorName,
drugName,
supplyDate,
offset,
pageSize);
return Action.SUCCESS;
}
This Action returns a JSON output and I access it using URL like:
http://localhost/app/searchAPI.action?name=UserName
then next time if I access using URL :
http://localhost/app/searchAPI.action
The field 'name' is till set to previous "UserName" value.
1.How to reset these values per request ?
2.I thought Action classes are instantiated per request,is it not ?
Problem was with the way Action classes are getting created by Spring.By default Spring creates singleton instances and for Struts2 ,Action classes also work as Model, due to this framework create a new instance of Action and place it in to values stack.
While creating action class using Spring make sure to define scope as prototype like
<bean id="patientSearchAPIClass"
class="com.axiohelix.nozoki.web.action.api.PatientSearch" scope=prototype>
So that new instance of Action should get created by Spring for each request.
I have a (request-scoped) list from which the user may select a "PQ" (list of links). When clicked or otherwise entered into the browser the main page for each PQ shall be displayed. Each PQ's page is of the form
http://localhost:8080/projectname/main.jsf?id=2
Here's the PQ bean first:
#Named
#ViewScoped
public class PqHome implements Serializable
{
#PersistenceContext(unitName="...")
private EntityManager em;
private Integer id;
private PQ instance;
#PostConstruct
public void init()
{
System.out.println("ID is " + id); // ID from URL param
instance = em.find(PQ.class, id);
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public PQ getInstance()
{
return instance;
}
}
Here's the main.xhtml:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
...>
<ui:define name="metadata">
<f:metadata>
<f:viewParam name="id" value="#{pqHome.id}">
<f:convertNumber integerOnly="#{true}" />
</f:viewParam>
<!--f:event type="preRenderView" listener="#{pqHome.init}" /-->
</f:metadata>
</ui:define>
<ui:define name="title">
<h:outputText value="Main" />
</ui:define>
...
</ui:composition>
Any time I select or otherwise refresh the page/URL I get a NullPointerException from the EntityManager:
org.jboss.weld.exceptions.WeldException: WELD-000049 Unable to invoke [method] #PostConstruct public de.mycomp.myproj.beans.PqHome.init() on de.mycomp.myproj.beans.PqHome#4f0ea68f
at org.jboss.weld.bean.AbstractClassBean.defaultPostConstruct(AbstractClassBean.java:595)
...
Caused by: java.lang.IllegalArgumentException: id to load is required for loading
at org.hibernate.event.spi.LoadEvent.<init>(LoadEvent.java:87)
at org.hibernate.event.spi.LoadEvent.<init>(LoadEvent.java:59)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:961)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:957)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:787)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:762)
at org.jboss.as.jpa.container.AbstractEntityManager.find(AbstractEntityManager.java:221)
at de.mycomp.myproj.beans.PqHome.init(PqHome.java:47)
... 56 more
[Line 47 is em.find(...)]
The line
<f:event type="preRenderView" listener="#{pqHome.init}" />
doesn't make things any better. I'm pretty desparate now.
How do you get URL GET request params into an #ViewScoped bean?
Note: I bet it's not a trivial thing to do. Chances are I'm doing something wrong here conceptually, so any tips on how to improve are welcome. I felt that I needed to choose #ViewScoped because there will be more complex AJAX-based GUI on that page which I'd really like to keep accessible via URL GET params.
Thanks
There is a better way to get id from url. Just use it in #PostConstruct init() method to get "id" from url:
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id");
You can still use ViewScoped and #PostConstruct.
The #PostConstruct is invoked directly after bean's construction and all dependency injection (such as #PersistenceContext, #EJB, #ManagedProperty, #Inject, etc..etc..).
The <f:viewParam> sets its value during the update model values phase, which is far after (post)construction of the bean. So inside the #PostConstruct the <f:viewParam> value is simply not yet been set. It'll be still null at that point.
You're close with <f:event type="preRenderView">, but you have to remove the #PostConstruct annotation.
So:
<f:viewParam name="pq" value="#{pqHome.id}">
<f:convertNumber integerOnly="#{true}" />
</f:viewParam>
<f:event type="preRenderView" listener="#{pqHome.init}" />
with
private Integer id;
public void init() {
instance = em.find(PQ.class, id);
}
Unrelated to the concrete problem, I'd suggest to use a Converter for this instead. See also Communication in JSF 2.0 - Converting and validating GET request parameters.
Also the combination #Named #ViewScoped won't work as intended. The JSF-specific #ViewScoped works in combination with JSF-specific #ManagedBean only. Your CDI-specific #Named will behave like #RequestScoped this way. Either use #ManagedBean instead of #Named or use CDI-specific #ConversationScoped instead of #ViewScoped.
This question already has answers here:
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
(5 answers)
Closed 8 years ago.
This converter is called from my JSF. I already register it inside faces-config.xml
public class ProjectConverter implements Converter{
#EJB
DocumentSBean sBean;
#ManagedProperty(value="#{logging}")
private Logging log;
public ProjectConverter(){
}
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
if(value.trim().equals("")){
return null;
}
return sBean.getProjectById(value);
}
public String getAsString(FacesContext context, UIComponent component, Object value)
{
if(value == null){
return null;
}
return String.valueOf(((Project) value).getId());
}
}
I ran into java.lang.NullPointerException, when I am in getAsObject(), the primary reason is because my Session Bean sBean is null. I dont know how to fix this, I need to access to my Session bean so that I can query from my database
As BalusC said, injection only works in managed beans. You can however declare your converter as a managed bean in your faces-config
<managed-bean>
<managed-bean-name>myConverter</managed-bean-name>
<managed-bean-class>com.example.MyConverter</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
And later reference it in a jsf component with an el expression:
<h:outputText value="#{myBean.value}" converter="#{myConverter}" />