values lost after validation error in JSF - jsf

I have two pages. Search page is the first page that takes user inputs. Second page shows the result set in datatable. Second page has 3 panel for resultset, update and create all in single page. Depending upon the which button being clicked, I am rendering panels true and false.
<h:panelGroup styleClass="panelGroup"
id="resultSet" rendered="#{bean.truefalse1}">
.
.
</h:panelGroup
<h:panelGroup styleClass="panelGroup"
id="updateForm" rendered="#{bean.truefalse2}">
.
.
</h:panelGroup
<h:panelGroup styleClass="panelGroup"
id="createForm" rendered="#{bean.truefalse3}">
.
.
</h:panelGroup>
From the search page I am setting these create and update panels to false and displaying only resultset.After the row from the result set is clicked I am showing
updateForm panel but keeping create panel to false.
But here the problem is, If there is validation error, then the property that was set from search page is being lost and all the panels are shown.
How do I get the value(boolean true or false) that was set from search page previously, since I am not navigating to different page.
I have getters and setters for boolean property in second class. I even tried keeping hidden fields(i.e the boolean property that was set from search page).
Shouldn't all the submitted values be recovered after validation error. Or just the ones we type in the form.
What is the best solution?
Any help is highly appreciated!!!

You indeed need to transfer the very same boolean properties to the next request. You can in theory use <h:inputHidden value="#{bean.boolean1}" /> for this, but unfortunately those will only be set during update model values phase, while you actually need it to be available during apply request values phase. Besides, they will also go lost when a validation error occurs.
There are three ways to fix this non-intuitive behaviour of h:inputHidden (I've ever filed a bug against it at the Mojarra issue list, but they didn't seem to do anything with it).
First is to use the binding on the h:inputHidden instead:
<h:inputHidden binding="#{bean.hidden1}" />
This however requires changes in the way you get/set the boolean values in the backing bean code. For example:
private HtmlInputHidden hidden1 = new HtmlInputHidden(); // +getter +setter.
public void setBoolean1(boolean boolean1) {
hidden1.setValue(boolean1);
}
public boolean getBoolean1() {
return (Boolean) hidden1.getValue();
}
Second is to use Tomahawk's t:saveState instead.
<t:saveState value="#{bean.boolean1}" />
The major advantage is that you don't need to change anything in the backing bean code. It will restore the value early before the apply request values phase. You only need to add extra libraries if not done yet, but as Tomahawk provides much more advantages than only the t:saveState, such as the in basic JSF implementation missing components/features t:inputFileUpload, t:dataList, t:dataTable preserveDataModel="true", t:selectOneRadio layout="spread" and so on, it is worth the effort.
The third way is to store them in a session scoped bean, but you actually don't want to do that for request scoped variables. It would only give "wtf?" experiences when the enduser has multiple tabs/windows open in the same session.
Edit: as per the comments, here's an SSCCE of the second way:
JSF page:
<%# taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%# taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<f:view>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<h:form id="form">
<h:inputHidden binding="#{myBean.hidden}" converter="javax.faces.Boolean" />
<h:commandButton value="submit" action="#{myBean.submit}"/>
<h:outputText value="Current boolean value: #{myBean.hidden.value}" />
</h:form>
</body>
</html>
</f:view>
MyBean class:
package mypackage;
import javax.faces.component.html.HtmlInputHidden;
public class MyBean {
private HtmlInputHidden hidden = new HtmlInputHidden();
public void submit() {
if (hidden.getValue() == null) {
hidden.setValue(true); // Set to true on 1st submit.
} else {
hidden.setValue(!((Boolean) hidden.getValue())); // Toggle true/false.
}
}
public HtmlInputHidden getHidden() {
return hidden;
}
public void setHidden(HtmlInputHidden hidden) {
this.hidden = hidden;
}
}
The relevant part of faces-config.xml:
<managed-bean>
<managed-bean-name>myBean</managed-bean-name>
<managed-bean-class>mypackage.MyBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Playground environment is JSF 1.2_13 on Tomcat 6.0.20.

Related

Is it possible to remove action from commandLink after it's been executed?

I have a table and in each row user can click on a link which triggers book availability check. So I have a commandLink with action and it works, but this action is executed every time user clicks on a link. I want it to be available only once. Also I don't want to hide link after click as it has onclick code which hides and shows details. Is it possible to remove action from commandlink after executing action?
The answer covered in Is it possible to use EL conditional operator in action attribute? is one of the ways that you can solve this. With that being said, since the release of JSF 2.2, there are also other alternatives. While removing the action attribute in JSF is problematic (it can be done with some trickery) - another solution is to use actionListeners together with an f:event binding that is connected to the preValidate event. This allows you to remove any of the connected actionListeners whenever you choose to do so.
Here is a complete solution with an event listener that modifies the component prior to it being processed for the view. Basically, you can do something like this;
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Dynamic action demo</title>
</h:head>
<h:body>
<h:form>
<h:dataTable var="row" value="#{removeActionBackingBean.rows}">
<h:column>#{row.primaryColumn}</h:column>
<h:column>#{row.hasBeenClicked}</h:column>
<h:column>
<h:commandButton actionListener="#{removeActionBackingBean.onPressed(row)}">
<f:attribute name="clicked" value="#{row.hasBeenClicked}"/>
<f:event listener="#{removeActionBackingBean.onModify}" type="preValidate" />
<f:ajax event="click" render="#form" />
</h:commandButton>
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
For the backing bean, here is a solution with a complete model (using Lombok);
#Data
#Named
#ViewScoped
public class RemoveActionBackingBean implements Serializable {
private List<Row> rows;
#PostConstruct
private void init() {
rows = new ArrayList<>();
for (int i = 0; i < 10; i++) {
rows.add(new Row(RandomStringUtils.randomAscii(10)));
}
}
public void onPressed(Row row) {
row.hasBeenClicked = true;
System.out.println(String.format("'%s' has been pressed!", row.primaryColumn));
}
public void onModify(ComponentSystemEvent event) {
final boolean isRowClicked = (boolean) event.getComponent().getAttributes().get("clicked");
if (isRowClicked) {
for (ActionListener al : ((UICommand) event.getComponent()).getActionListeners()) {
((UICommand) event.getComponent()).removeActionListener(al);
}
}
}
#Data
#RequiredArgsConstructor
public class Row {
private #NonNull String primaryColumn;
private boolean hasBeenClicked;
}
}
The key sections to look at is f:event and the onModify() method binding. As you can see, we simply check if a certain "row" is considered as clicked - if this is the case, we clear all the actionListeners currently defined on the component. Effectively, there will be no actionEvent called when the button is pressed.
While the above solution modifies the actionListeners of a button, it can be adopted and used for other types of components and when you want to modify certain attributes of a component based on some condition - so it's extremely useful to know this trick.

Hide a datatable column without rerendering it. JSF Client side manipulation

I used to develop rich client interface applications using Spring MVC mixed with jQuery and html; However, because we don't have a good designer currently, I thought to go with JSF 2.2 using PrimeFaces 5.2 which should give me professional interface without a regular designer.
I have good knowledge about how to use JSF as a component based framework, but my concern is that I want to avoid unnecessary calls to the server as much as possible since I have tens of thousands of requests daily. So, I adhered to use jQuery with JSF and will avoid ajax calls as much as possible to update the view, unless I need to completely restructure it based on some user selections.
Here is the scenario I tried:
I have a table from which the user can select to hide/show columns based on checkboxes selection per each column. Now, as I said I don't want to use ajax to rerender the view for each select/unselect and I used jQuery instead.
After I made my first draft page and tried to play around as I used with jQuery, I found that I had nastily to navigate the page source using firebug and build complex jQuery to hide p:dataTable columns.
Is that normal? I mean, when I look to the code and see how much it depends on Primefaces html structure generation, I feel unconfident.
Am I using JSF unwisely or this is the truth of JSF?
You can use all the jQuery/JS you want over the JSF views. The main problem is that model doesn't keep updated, as long as you don't sync the state. To see that hiding/showing a column isn't that complex, here you've got a basic full-working example of it using jQuery and its style selector:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head />
<h:body>
<script>
function switchColumn() {
if ($('.style1').is(":visible")) {
$('.style1').fadeOut(1000);
} else {
$('.style1').fadeIn(1000);
}
}
</script>
<h:form>
<p:commandButton type="button" onclick="switchColumn();"
value="Switch" />
<p:dataTable var="test" value="#{testBean.values}">
<p:column styleClass="style1">
#{test.id}
</p:column>
<p:column>
#{test.val1}
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
#ManagedBean
#ViewScoped
public class TestBean {
private List<TestClass> values = new ArrayList<TestClass>();
public TestBean() {
values.addAll(Arrays.asList(new TestClass(1, "val1"), new TestClass(2,
"val2")));
}
public List<TestClass> getValues() {
return values;
}
public class TestClass {
private Integer id;
private String val1;
public TestClass(Integer id, String val1) {
this.id = id;
this.val1 = val1;
}
public Integer getId() {
return id;
}
public String getVal1() {
return val1;
}
}
}
It must be said JSF is designed to control the request contents and the model updates for you. Is not as light as SpringMVC in these terms, as you cannot choose what to send (not totally). So, if being worried about request/response performaces, maybe you should go with other framework.
See also:
Is there a way to add columns datatable without rerendering the entire table

Wrong Character Set for JSF's h:inputText on first submit (only)

In the following form, we try to return a user's input to JSF's h:inputText or PrimeFaces' p:inputText.
We experience strange behavior when non-Latin characters (Japanese, Hebrew, etc. ) are entered:
On first request we get unrecognized character set, but on the second request - we get a correct result.
Input/Output Examples (first run only):
Japanese:
input = 日
output = æ¥
Hebrew:
input = א
output = ×
JSF:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.prime.com.tr/ui">
<body>
<h:form>
<h:outputLabel value="Name:"/>
<h:inputText value="#{newTestController.registeredCustomerFirstName}"/>
<h:commandButton value="Continue" action="#{newTestController.RegisteredNewCustomer(actionEvent)}"/>
</h:form>
</body>
</html>
Backing Bean:
#ManagedBean(name = "newTestController")
#SessionScoped
public class NewTestController {
private String registeredCustomerFirstName;
public String getRegisteredCustomerFirstName() {
return registeredCustomerFirstName;
}
public void setRegisteredCustomerFirstName(String registeredCustomerFirstName) {
this.registeredCustomerFirstName = registeredCustomerFirstName;
}
public void RegisteredNewCustomer(ActionEvent actionEvent) throws Exception {
}
}
As commented above - it is needed to define a default-charset for the application server.
For glassfish: add <parameter-encoding default-charset="UTF-8" /> to glassfish-web.xml.
For other application servers see BalusC's blog regarding this issue.
This is related to < http://java.net/jira/browse/GLASSFISH-18007 >. That fix was made to prevent a warning message when we unconditionally set the encoding to UTF-8, which would seem to be what we want, but in this case we felt it safer to not do it.
I've created a related issue in Mojarra, < http://java.net/jira/browse/JAVASERVERFACES-2217 >. Bottom line: setting the encoding explicitly in the app configuration is the right solution. The implementation is already doing the right thing.
Specifying charset in the config file might be not enough.
Try using p:commandButton instead of h:commandButton. The p:commandButton by default uses ajax, while the h:commandButton does non-ajax submit.

JSF viewscope values not getting displayed in the view properly

I have a managed bean under ViewScope. It has an instance variable inside it.
MetaData object has a inputItem object List.
#ManagedBean
#ViewScoped
public class ConBean implements Serializable {
private MetaData metadata;
#PostConstruct
#SuppressWarnings("unchecked")
public void init() throws IOException {
this.metadata = new MetaData ();
}
public void proc(){
List<InputItem> inputs= new ArrayList<InputItem>();
inputs.add(***** code to populate the inputItem List);
//after populating, inputs added to the metadata
metadata.setInputs(inputs);
}
//getters & setters
}
in my JSF , input list is populated inside a UI repeat.
<div id="inputplaceholder">
<ui:repeat value="#{conBean.metaData.inputs}" var="content">
</ui:repeat>
</div>
the div inputplaceholder is periodically updated using a richfaces poll.
<a4j:poll id="poll" interval="12000" action="#{conBean.proc}"
execute="#form" render="inputplaceholder"/>
The problem that I have is even though inputItems are set to the metaData object correctly inside the proc() method, when the view is rendered/partially updated, it doesn't get highlighted in the UI. so partial update takes no effect. I tried moving
this.metadata = new MetaData ();
inside the proc method but had no luck.
any ideas and help is highly appreciated.
thanks ...
Did the partial render really take place? This is impossible. There is namely no JSF component with the ID inputplaceholder. You assigned it to a plain HTML <div> element. Replace it by a fullworthy JSF component:
<h:panelGroup layout="block" id="inputplaceholder">
Also, since you used a relative ID in the render attribute, it will only scan for components in the same parent naming container component. The <ui:repeat> is such one, however the component with the desired ID is placed outside it. You'd like to use an absolute ID instead. Assuming that it's inside a <h:form> with a fixed ID:
<h:form id="myform">
<h:panelGroup layout="block" id="inputplaceholder">
...
then you should be referencing it in the render attribute as follows
render=":myform:inputplaceholder"

JSF UIInput in DataTable Footer

I am adding (UIInput) to the footer columns (UIColumn) of a datatable (UIData) created dynamically. The UIData is bound to a datatable tag in the jsp.
In the datatable, I just have headers and footers with the header having the labels and footer having the corresponding value in editable textbox.
When I change the value and submit the form using a commandButton and I try to access the UIInput value using .getValue() in the action method, I just get the old values and not the values updated in the page.
I tried binding it to an attribute in the backing bean and checked the values being set in the setter. I notice that the old values are being set and the values I updated in the page do not reflect in the action method or setter.
I tried using .getValue, .getLocalValue, .getSubmittedValue. None of these give me the new values.
Any suggestions what I might be doing worng?
I managed to workaround by pulling the values from requestParameterMap.
If there is a fix for the issue please do let me know.
McDowell - thanks for your inputs.
I tried running your demo code code under MyFaces 1.2.3 on Tomcat and Mojarra 2.0.0 Beta on Glassfish, but was unable to reproduce the problem - the save() method printed the values I entered into the fields.
(To use MyFaces, I had to change new UIData() to new HtmlDataTable(), probably due to how they implement the table renderer, but that is a minor change.)
I will note a couple of things about the bean:
the table getter will keep adding columns every time it is called - like on a page refresh with server-side state saving
keeping a reference to a UIComponent in a session bean usually is not a good idea; you would be better off using request scope for component bindings
session beans are supposed to implement Serializable (though I realize not everyone does this) and UIComponents cannot be serialized
your component might end up in multiple views if the user opens the page twice - concurrency issues
according to the spec: when JSF creates the view, it will use the component bound via the getter; but, when it restores the view (on submit), it will set the component via the setter, so keeping a reference is (at best) redundant
You might want to change the getter to something like this:
private UIData headerDataTable;
public UIData getHeaderDataTable() {
if (headerDataTable == null) {
headerDataTable = new UIData();
getHeaderTable(headerDataTable);
}
return headerDataTable;
}
I am not confident that these changes will fix your issue, though - if you are still having trouble, try again with more details - your JSF implementation, the version, and the value of the javax.faces.STATE_SAVING_METHOD parameter in web.xml (if any).
The actual code does several other processing, but below code should help in replicating the issue. In the below code, I expect the TestString to output the modified values from the page. But it just returns old values.
Below is the jsp:
<%#taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%#taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<html>
<head>
</head>
<f:view>
<body>
<h:form styleClass="form" id="form1">
<h:commandButton value="Save" action="#{TestPageBackingBean.save}" styleClass="commandExButton"/>
<h:outputText styleClass="label" value="Header Table"/>
<h:dataTable binding="#{TestPageBackingBean.headerDataTable}"></h:dataTable>
</h:form>
</body>
</f:view>
</html>
Below is the faces config:
<managed-bean>
<managed-bean-name>TestPageBackingBean</managed-bean-name>
<managed-bean-class>test.jsf.TestPageBackingBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Below is the backing bean code:
package test.jsf;
import java.io.IOException;
import javax.faces.component.UIColumn;
import javax.faces.component.UIData;
import javax.faces.component.UIInput;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
public class TestPageBackingBean {
private UIData headerDataTable = new UIData();
public TestPageBackingBean() {
}
public UIData getHeaderDataTable()
{
return getHeaderTable(headerDataTable);
}
public UIData getHeaderTable(UIData dataTable)
{
for (int i=0;i<10;++i)
{
dataTable.getChildren().add(getColumn(i));
}
return dataTable;
}
private UIColumn getColumn(int i)
{
UIOutput outputLabelText = new UIOutput();
UIInput inputFieldText = new UIInput();
UIColumn column = new UIColumn();
outputLabelText.setValue("Label" + i);
inputFieldText.setValue("test input" + i);
column.setHeader(outputLabelText);
column.setFooter(inputFieldText);
return column;
}
public String save() throws IOException {
String TestString = "";
FacesContext ctx = FacesContext.getCurrentInstance();
if (!ctx.getResponseComplete()) {
for (int i=0; i<headerDataTable.getChildren().size();++i)
{
TestString = TestString + (String)((UIInput)((UIColumn) headerDataTable.getChildren().get(i)).getFooter()).getValue();
}
System.out.println(TestString);
}
return "save";
}
public void setHeaderDataTable(UIData headerDataTable) {
this.headerDataTable = headerDataTable;
}
}
The issue is not fully resolved yet.
I use RSA 7, with IBM JSF - Base Faces Support 7.0 and Enhanced Faces Components 7.0 on WAS 6.0 The javax.faces.STATE_SAVING_METHOD was 'server' by default.
I tried changing STATE_SAVING_METHOD to 'client'. It did print the changed value in the output but in label4 instead of label0 which I modified. On the next submit the value moved from label4 to label8. Seemed inconsistent.

Resources