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.
Related
I have a custom component that implements UIInput and that needs to save some state info for later reuse in postback requests. Used standalone it works fine, but inside an <ui:repeat> the postback finds the saved state of the latest rendered row of data. The log output of an action call is
INFORMATION: myData is "third foo"
INFORMATION: myData is "third foo"
INFORMATION: myData is "third foo"
INFORMATION: ok action
where I would expect
INFORMATION: myData is "first foo"
INFORMATION: myData is "second foo"
INFORMATION: myData is "third foo"
INFORMATION: ok action
I understand that myComponent is a single instance inside of ui:repeat. So what is the best way to save component state so it is restored correctly for each row in the dataset?
My XHTML form:
<h:form>
<ui:repeat var="s" value="#{myController.data}">
<my:myComponent data="#{s}"/>
</ui:repeat>
<h:commandButton action="#{myController.okAction}" value="ok">
<f:ajax execute="#form" render="#form"/>
</h:commandButton>
</h:form>
My Bean:
#Named
#ViewScoped
public class MyController implements Serializable {
private static final long serialVersionUID = -2916212210553809L;
private static final Logger LOG = Logger.getLogger(MyController.class.getName());
public List<String> getData() {
return Arrays.asList("first","second","third");
}
public void okAction() {
LOG.info("ok action");
}
}
Composite component XHTML code:
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:cc="http://xmlns.jcp.org/jsf/composite">
<cc:interface componentType="myComponent">
<cc:attribute name="data"/>
</cc:interface>
<cc:implementation>
<h:panelGrid columns="2">
<h:outputLabel value="cc.attrs.data"/>
<h:outputText value="#{cc.attrs.data}"/>
<h:outputLabel value="cc.myData"/>
<h:outputText value="#{cc.myData}"/>
</h:panelGrid>
</cc:implementation>
</ui:component>
Composite Component backing class:
#FacesComponent
public class MyComponent extends UIInput implements NamingContainer {
private static final Logger LOG=Logger.getLogger(MyComponent.class.getName());
public String calculateData() {
return String.format("%s foo", this.getAttributes().get("data") );
}
public String getMyData() {
return (String)getStateHelper().get("MYDATA");
}
public void setMyData( String data ) {
getStateHelper().put("MYDATA", data);
}
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
this.setMyData( calculateData() );
super.encodeBegin(context);
}
#Override
public void processDecodes(FacesContext context) {
super.processDecodes(context);
LOG.log(Level.INFO, "myData {0}", getMyData() );
}
}
Just tried reproducing your issue and yes, now I get what you're after all. You just wanted to use the JSF component state as some sort of view scope for the calculated variables. I can understand that. The observed behavior is indeed unexpected.
In a nutshell, this is explained in this blog of Leonardo Uribe (MyFaces committer): JSF component state per row for datatables.
The reason behind this behavior is tags like h:dataTable or ui:repeat only save properties related with EditableValueHolder interface (value, submittedValue, localValueSet, valid). So, a common hack found to make it work correctly is extend your component from UIInput or use EditableValueHolder interface, and store the state you want to preserve per row inside "value" field.
[...]
Since JSF 2.1, UIData implementation has a new property called rowStatePreserved. Right now this property does not appear on facelets taglib documentation for h:dataTable, but on the javadoc for UIData there is. So the fix is very simple, just add rowStatePreserved="true" in your h:dataTable tag:
In the end, you have basically 3 options:
Use UIInput#value instead of something custom like MYDATA
As instructed by the abovementioned blog, just replace getMyData() and setMyData() by the existing getValue() and setValue() methods from UIInput. Your composite component already extends from it.
#Override
public void encodeBegin(FacesContext context) throws IOException {
this.setValue(calculateData()); // setValue instead of setMyData
super.encodeBegin(context);
}
#Override
public void processDecodes(FacesContext context) {
super.processDecodes(context);
LOG.log(Level.INFO, "myData {0}", getValue() ); // getValue instead of getMyData
}
And equivalently in the XHTML implementation (by the way, the <h:outputText> is unnecessary here):
<h:outputText value="#{cc.value}" /> <!-- cc.value instead of cc.myData -->
However, this didn't really work when I tried it on Mojarra 2.3.14. It turns out that Mojarra's implementation of the <ui:repeat> indeed restores the EditableValueHolder state during restore view (yay!), but then completely clears out it during decode (huh?), turning this a bit useless. I'm frankly not sure why it is doing that. I have also found in Mojarra's UIRepeat source code that it doesn't do that when it's nested in another UIData or UIRepeat. So the following little trick of putting it in another UIRepeat attempting to iterate over an empty string made it work:
<ui:repeat value="#{''}">
<ui:repeat value="#{myController.data}" var="s">
<my:myComponent data="#{s}" />
</ui:repeat>
</ui:repeat>
Remarkably is that nothing of this all worked in MyFaces 2.3.6. I haven't debugged it any further.
Replace <ui:repeat> by <h:dataTable rowStatePreserved="true">
As hinted in the abovementioned blog, this is indeed documented in UIData javadoc. Just replace <ui:repeat> by <h:dataTable> and explicitly set its rowStatePreserved attribute to true. You can just keep using your MYDATA attribute in the state.
<h:dataTable value="#{myController.data}" var="s" rowStatePreserved="true">
<h:column><my:myComponent data="#{s}" /></h:column>
</h:dataTable>
This worked for me in both Mojarra 2.3.14 and MyFaces 2.3.6.
This is unfortunately not supported on UIRepeat. So you'll have to live with a potentially unnecessary HTML <table> markup generated by the <h:dataTable>. It was during JSF 2.3 work however discussed once to add the functionality to UIRepeat, but unfortunately nothing was done before JSF 2.3 release.
Include getClientId() in state key
As suggested by Selaron in your question's comments, store the client ID along as key in the state.
public String getMyData() {
return (String) getStateHelper().get("MYDATA." + getClientId());
}
public void setMyData(String data) {
getStateHelper().put("MYDATA." + getClientId(), data);
}
Whilst it's a relatively trivial change, it's awkward. This does not infer portability at all. You'd have to hesitate and think twice every time you implement a new (composite) component property which should be saved in JSF state. You'd really expect JSF to automatically take care of this.
I'm looking for some piece of code for setting a property in a JSF managed bean. My first idea was something like that:
<c:set var="#{loginBean.device}" value="mobil"></c:set>
That means I want to set the attribute device to the value "mobil" without a button have to been clicked.
Yes, you can use c:set for this purpose.
<c:set value="mobil" target="#{loginBean}" property="device" />
Doc: http://docs.oracle.com/cd/E17802_01/j2ee/javaee/javaserverfaces/2.0/docs/pdldocs/facelets/c/set.html
However, setting a static value rarely makes sense. You might consider to set a default value directly in your managed bean class. Also in terms of maintainability since you can handle constants better in the Java code than in the view layer.
I think you want the JSF tag child tag setPropertyActionListener. You can set this as a child tag in any ActionComponent.
<h:anyActionComponent id="component1">
<f:setPropertyActionListener target="#{loginBean.device}" value="mobil" />
</h:anyActionComponent>
UPDATE:
I originally misunderstood the users problem. They have a page, and they want a property to be set when the page loads. There is a couple ways to do this, but both are a little different. If you want to set a property to a value after every postback then you can use the #PostConstruct annotation on a ManagedBean method.
#PostConstruct
public void initializeStuff() {
this.device = "mobil";
}
Now if I have a ViewScoped or SessionScope bean that needs to be initialized with a default value just once when the page loads then you can set a phase lifecycle event that will run after every postback and check to see if the page should be initialized or not.
mah.xhmtl:
<f:event listener="#{loginBean.initialize()}" type="preRenderView" />
LoginBean:
public void initialize() {
if (this.device == null)
this.device = "mobil";
}
I am not able to Comment: If you need the value to be ready on page on load, you could use Managed Bean to directly initialize value or use its constructor or #PostConstruct to do the same.
#ManagedBean
#ResquestScoped
public class LoginBean {
private String device = "some value";
//Using Constructor
public LoginBean() {
device = getvalueFromSomewhere();
}
//Using PostConstruct
#PostConstruct
public void init() {
device = getvalueFromSomewhere();
}
}
Instead of setting the value in the xhtml file you can set via another ManagedBean. For instance if you have managedBean1 which manages page1.xhtml and managedBean2 which manages page2.xhtml. If page1.xhtml includes page2.xhtml like:
<ui:include src="page2.xhtml"/>
in managedBean1 you can have at the top
#ManagedProperty(value = "#{managedBean2}")
private ManagedBean2 managedBean2;
and in the PostConstruct
#PostConstruct
public void construct() {
managedBean2.setProperty(...);
}
worked for me anyway...
I have a view-scoped bean ManageFoo.java:
#ManagedBean(name = "ManageFoo")
#ViewScoped
public class ManageFoo {
#ManagedProperty(value = "#{currentRow}")
private Foo currentRowBean;
.
.
public void setCurrentRowBean(Foo foo) {...}
public Foo getCureentRowBean() {...}
public void edit(ActionEvent e) {
getCurrentRowBean();
.
.
}
}
I then have the facelets file ManageFoo.xhtml:
<h:dataTable value=#{ManageFoo.foos} var="currentRow">
<h:column>
<h:commandLink actionListener="#{ManageFoo.edit}"/>
.
.
When the 'commandLink' is clicked, however, 'getCurrentRowBean' returns null.
I have this working fine using FacesContext and a getBean("currentRow") helper method, but I'm trying to "JSF 2ify" my web application by using ManagedProperty's, ManagedBean's, etc. Am I just not implementing this properly or am I trying to use ManageProperty's in a way that doesn't make sense?
After feedback from Balus, his solution works well for my action methods, but I'm having trouble getting it to work for methods that return values (such as boolean). The following method is used in a bunch of "rendered" attributes for each row. For example, if you are editing a certain row, all of the other edit buttons on the other rows disappear and the edit button for the current row is replaced by a cancel edit button and save button. So, this method has different return results for each row:
public boolean isCurrentRowEditing() {
if(getCurrentRowDataBean().equals(getCurrentDataEditing())) {
return true;
} else {
return false;
}
}
Since I'm trying to eliminate the "getCurrentRowDataBean()" method everywhere, I need a way for this to work.
The #ManagedProperty is set directly after bean's construction. At the point the bean is been constructed, JSF is definitely not busy iterating over the datatable. Also, your bean is put in view scope, so it's been set when the view is requested for the first time, not when you submit the form to the same view. The #{currentRow} will always be null.
There are several ways to achieve this. If you're running an EL 2.2 capable container or are using JBoss EL, then you could just pass it to the action (not listener!) method:
<h:commandLink action="#{manageFoo.edit(currentRow)}"/>
(note that I fixed your managed bean name to start with lowercase, conform the coding conventions)
with
public void edit(Foo currentRow) {
// ...
}
If you aren't running an EL 2.2 capable container nor can't use JBoss EL, then you need to bind the datatable's value to DataModel and use DateModel#getRowData() inside the action(listener) method to obtain the current row.
See also:
How can I pass selected row to commandLink inside dataTable? (shows all possible ways)
Recommended JSF 2.0 CRUD frameworks (shows DataModel way)
Update backing bean in datatable from h:commandButton and JPA (shows EL 2.2 way)
Update as per your update, just compare it in EL. E.g.
rendered="#{currentRow == manageFoo.currentRow}"
and
rendered="#{currentRow != manageFoo.currentRow}"
(note that this only works if its equals() is properly implemented!)
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"
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.