I have a #ViewScoped #ManagedBean which creates an unique ID. This ID is rendered in a form like:
<h:form>
<h:outputText value="#{myBean.uid}" id="uid"/>
<h:hiddenInput value="#{myBean.uid}" id="hiddenId" />
....
<p:commandButton actionListener="#{myBean.create}" ajax="true" update="#form" value="Create" />
</h:form>
So far so good. On first request the page is rendered correctly. After submitting the form and in the case of validation failure, the outputText is empty but the hidden input field keeps its variable.
Any clue what I'd have to do to prevent this behavior and too let the outputText keep its state?
I realized that the bean seems to be initialized after each ajax request. But then, why does the hidden input field keeps the old variable?
Here is the relevant code of my bean:
#ManagedBean(name = "myBean", eager = true)
#Stateful
#Model
#ViewScoped
public class MyBean implements Serializable {
...
private String uid;
...
#PostConstruct
public void initWithData() {
this.uid = UUID.randomUUID().toString();
}
}
JSF input components have 3 places where the value (the state) is stored:
Submitted value (the raw unconverted/unvalidated String request parameter value).
Local value (the successfully converted/validated object value, stored inside component itself).
Model value (when the entire form is successfully processed, stored as bean property)
JSF processes input components as follows:
Get HTTP request parameter value by component's client ID and store it as submitted value.
If conversion/validation succeeds, set it as local value and set submitted value to null.
If entire form is successfully processed, set it as model value and set local value to null.
JSF renders values of input components as follows:
If submitted value is not null, display it.
Else if local value is not null, display it.
Else display model value.
So, in your particular case of a general validation failure, JSF is for that hidden input component just redisplaying the local value instead of the model value. If you want to achieve the same with the output text, I think best is to just do the same as JSF:
<h:outputText value="#{empty hiddenId.submittedValue ? empty hiddenId.localValue ? hiddenId.value : hiddenId.localValue : hiddenId.submittedValue}" />
<h:inputHidden id="hiddenId" binding="#{hiddenId}" value="#{myBean.uid}" />
Alternatively, you could just use a read only input and remove the border by CSS if necessary:
<h:inputText id="hiddenId" value="#{myBean.uid}" readonly="true" style="border: none;" />
As to your bean, I'm not sure what's happening there as this class seems to be extremely tight coupled. I'd rather split it into 3 classes: one real backing bean, one stateless service and one model entity. Further, you should also make sure that you aren't binding view build time tags or attributes to a property of a view scoped bean. Otherwise it will indeed guaranteed be reconstructed on every single request.
See also:
JSTL in JSF2 Facelets... makes sense?
#PostConstruct method is called even if the ManagedBean has already been instantiated (e.g. on AJAX-calls)
By the way, the eager=true has only effect in #ApplicationScoped beans.
The problem is probably the #Model stereotype (was, as you already removed it). It combines #Named and #RequestScoped and makes the bean a CDI request scoped bean. CDI managed beans should be resolved before JSF managed beans and therefore the #ViewScoped has no effect (it creates a JSF managed bean).
Related
I am using JSF 2 (MyFaces 2.2.9) and Bean Validation (Hibernate Validator 5.2.2.Final).
I use the #NotNull annotation on a bean that is a property of my managed bean.
(ManagedBean class)
#ManagedBean
public class ConfiguracionesBean {
private Configuraciones configuraciones;
#PostConstruct
public void postConstruct() {
this.configuraciones = Configuraciones.find();
}
//getters/setters/unrelevant code
...
}
(Bean class)
public class Configuraciones {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotNull
private String name;
}
(.xhtml page)
<h:outputLabel for="name">#{i18n['name']}</h:outputLabel>
<h:inputText id="name" label="#{i18n['name']}" value="#{configuracionesBean.configuraciones.name}" />
<h:message for="name" errorClass="error" warnClass="warn" infoClass="info"/>
The first time the page is displayed, the "name" input text field contains the value that was previously stored in my database (normal until here).
If I delete this value on the html form, and I submit, then the #NotNull constraint is triggered, and I get an error (still normal here).
But the value of the inputText displayed on the html page is the value from the managed bean as it was initialized). Not the "empty" value that the user just put (by emptying the field). So the user sees an error "The field can not be null", but the field actually contains the previous value.
Is there a way to prevent this? I want the input to be empty, as it is what the user put.
I precise that I have the option :
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Thanks in advance for you answers.
Note : I would really like to stick with the validations only done by JSR 303, as like this I have a uniform way of dealing with constraints. So no JSF validators (that could do the same, I know).
Alex
This is the normal behaviour. Validations are processed in JSF's "Process Validations Phase". This phase is computed before the "Update Model Values" phase. That means if validation fails your model (Bean) won't be updated with the invalid values. All phases after "Process Validations Phase" will be skipped and "Render Response Phase" will be executed. The result is that when your invalid field is rerendered it will still show the old value because the model has not been updated.
In the "Render Response Phase" you will now be able to show an error message with <h:messages>. The only way I can think of to prevent old model values from showing is using the ajax-Element and not rendering the input element in the render response phase but instead just rendering its error message.
So in your xhtml:
<h:outputLabel for="name">#{i18n['name']}</h:outputLabel>
<h:inputText id="name" label="#{i18n['name']}" value="#{configuracionesBean.configuraciones.name}">
<f:ajax event="valueChange" render="errorBlock" />
</h:inputText>
<h:panelGroup id="errorBlock">
<h:message for="name" errorClass="error" warnClass="warn" infoClass="info"/>
</h:panelGroup>
The following links might be helpful
JSF life cycle illustrated: http://developersbook.com/jsf/images/JSF-Lifecycle.png
JSF life cycle: https://docs.oracle.com/javaee/1.4/tutorial/doc/JSFIntro10.html#wp122256
I have following request scoped Spring bean with #postconstruct method init():
#Component
#Scope("request")
public class editUserBB {
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
private UserDto user;
#Autowired
private IUserService userService;
#PostConstruct
public void init() throws IOException {
String id_string = params.get("id");
Long id = Long.parseLong(id_string);
user = userService.getUserById(id);
}
public String save(){
// save to database
return "user?faces-redirect=true&id=" + (long)user.getId();
}
}
And userEdit.xhtml with h:form and commandButton:
<h:commandButton value="Save" action="#{editUserBB.save()}" />
However, after the save button is clicked the init() method is called once again annulling all the changes made to the UserDto object before I can save it to the DB. So what am I doing wrong?
And I just tested, the init() method is called even before save(), which I also don't understand..
That's not the fault of the init() method. That's just your own fault of placing the bean in the request scope instead of in the view scope.
A request scoped bean lives as long as a single HTTP request-response cycle. Opening the page with the form counts as one HTTP request. The HTTP request is garbaged when the associated HTTP response is finished sending the result to the client, including all associated request scoped beans. Submitting the form counts as another HTTP request which thus creates a completely new instance of the request scoped bean. If you explore/debug the instance's hashcode (and constructor), you'll notice that it are actually two physically completely distinct instances. There's thus absolutely no means of the init() to "override" the old values. The old values aren't there in first place.
JSF has solved this awkward behavior of a request scoped bean with introducing the view scope. You probably have ever heard/read about it before replacing JSF bean management facility by Spring's one for some reason. Spring bean management facility doesn't have a native concept of the view scope, you'd need to homegrow one.
If you intend to stick to Spring bean management facility, then your best bet is retaining the request parameter responsible for proper initialization of the data along with the form submit. You can use <f:param> for that:
<h:commandButton value="Save" action="#{editUserBB.save()}">
<f:param name="id" value="#{param.id}" />
</h:commandButton>
See also:
How to choose the right bean scope?
How do request/session/application work?
Unrelated to the concrete problem, you should avoid calling FacesContext during instance construction/initialization. This is bad design. In this particular case, move that line of obtaining the request parameter map to inside the init() method. Also here, JSF bean management facility has a standard solution in flavor of #ManagedProperty while Spring one doesn't have.
<h:form>
<h:selectManyMenu id="carsList"
value="#{bean.carList}"
style="width:400px; height:100px" label="List">
<f:selectItems value="#{bean.cars}" var="i"
itemLabel="#{i.code}" itemValue="#{i.Name}" />
<f:selectItem itemLabel="Other" itemValue="other"/>
<f:ajax event="change" execute="#this" render="othermodel"/>
</h:selectManyMenu>
<br></br>
<h:panelGroup id="othermodel">
<h:outputText value="Others:" style="margin-top:10px;color:red;font-weight:bold;"
rendered="#{bean.carList.contains('other')}" />
<h:inputText size="50" id="otherinput" required="true"
rendered="#{bean.carList.contains('other')}"/>
<h:message for="otherinput"></h:message>
</h:panelGroup>
<h:commandButton value="Next" action="#{bean.submitForm}"/>
</h:form>
My bean is requestScoped and when my carList has a value other i am able to show the panelGrid but when user don't enter any value in the input field rendered using AJAX , even i specificed required=true but it's not getting validated. And value of the input text box is null in the backend code.
How can I do the validation for the input field rendered using AJAX and why my value is coming as null? I am using JSF 2.0
The rendered attribute is re-evaluated during the request of the form submit. Your bean is request scoped and thus recreated on every single request (also ajax requests) with all properties set to default. So the carList property will also be emptied and the rendered attribute would evalute false before the update model values phase has filled the carList. So, for JSF, the <h:inputText> is never rendered and thus it won't be processed.
Put the bean in the view scope instead. It'll live as long as you interact with the same view by (ajax) postbacks. It's the right scope for dynamic ajax based forms with a lot of conditional rendering.
See also:
How to choose the right bean scope?
I tried this with #ViewScope. When I made my Spring managed service as transient I was getting null reference after deserialization. So, i tried the below method to get the Spring Service.
#ManagedBean(name="bean")
#ViewScoped
public class Bean implements Serializable {
#ManagedProperty(value="#{appService}")
private transient AppService appService;
// Getting AppService Object which is singleton in the application during deserialization
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
FacesContext context = FacesContext.getCurrentInstance();
appService = (AppService)context.getApplication()
.evaluateExpressionGet(context, "#{appService}", AppService.class);
stream.close(); // not sure i have to do this
}
}
This is working for me without any issues.
imagine there is BeanA bound to ViewA.xhtml and BeanB bound to ViewB.xhtml. BeanA and BeanB are backing beans with just one attribute per bean: "Object obj;" with getters and setters.
Now i am in ViewA doing the following:
<ui:repeat value="#{someOtherBean.listOfObjects}" var="objSelected">
<h:commandLink value="someValue" action="ViewB">
<f:setPropertyActionListener target="#{beanB.obj}" value="#{objSelected}" />
</h:commandLink>
</ui:repeat>
This works great! I iterate through a List and the object i click on (via commandLink) is set into BeanB.
Problem: I need to work with a GET Request, so i have so use h:link or h:outputLink (btw, where is the difference?). But doing so, i can't use the f:setPropertyActionListener component, because a GET request does not fire an action event.
Since there is no action event i have to use a GET request, giving the ID of the object to BeanB as a parameter. BeanB then uses a dao to get the object with the specified ID. But this sux. I don't want to use the dao. So my specific question:
What is the equivalent to f:setPropertyActionListener for GET request links? How can I set a property of a BackingBean when I click on a h:link or h:outputLink component?
I hope there is a solution, thanks in advance.
Pass it as a request parameter. Request parameters can be only strings, so you need to convert it (or better, the smallest possible part which uniquely identifies the object in question) to string first.
Here's an example which uses the (database) identifier of the object for this:
<ui:repeat value="#{someOtherBean.listOfObjects}" var="objSelected">
<h:link outcome="ViewB">
<f:param name="id" value="#{objSelected.id}" />
</h:link>
</ui:repeat>
And in BeanB add:
#ManagedProperty("#{param.id}")
private Long id;
private SomeObject objSelected;
#PostConstruct
public void init {
objSelected = objService.find(id);
}
// ...
Consider the following page (header and stuff omitted for brevity):
<h:body>
<h:form>
<h:inputText
id="theInput"
value="#{theBean.text}">
<f:ajax render="theButton" />
</h:inputText>
<h:commandButton
id="theButton"
value="Result"
disabled="#{theBean.disabled}"
action="result" />
</h:form>
</h:body>
TheBean looks like this:
#Named
#RequestScoped
public class TheBean {
String text;
public TheBean() {
this.text = "test";
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public boolean isDisabled() {
return this.text.equals("test");
}
}
result.xhtml is a placeholder that displays some static text.
The idea is to enable the button only if the entered text is valid (in this case different from the default). It works, and the button is correctly enabled if I enter something other than test, but when I click on the button, it does not navigate to result. However, it works if I do one of the following:
I make the button always enabled. This baffles me, because in the example above the button is correctly enabled, it only does not navigate to the next page. Why would that change if I leave it enabled all the time?
I change the scope of TheBean to #SessionScoped. I do not understand this either. How can the scope of the backing bean influence the page-navigation? Everything else in the example seems to work just the same with #RequestScoped.
I would really like to keep TheBean request-scoped. Is there any way to make it the navigation work without making it session-scoped?
Your bean is request scoped which means that every single HTTP request (also ajax requests!) creates a brand new bean. When you change the input, then the ajax request creates a new bean and sets the value. When you press the button, then the normal request creates a new bean (thus the value set by ajax is lost!) and determines if the button is eligible to be invoked -which is not- and thus won't invoke the action.
The view scope is designed to overcome exactly this kind of problems. Put the bean in the view scope.
#ManagedBean
#ViewScoped
public class TheBean {
This way the bean will live as long as you're interacting with the same view by either normal or ajax requests.
Sorry, I don't know how from top of head what the proper CDI declaration is. It's at least called "conversation scope".