I have a dataTable that lists some objects and I want to set a property for those objects using a selectOneListbox. This is my dataTable
<h:dataTable value="#{someHandler.entities}"
binding="#{someHandler.dataTable}" var="entitiy">
<h:column>
<f:facet name="header">
<t:outputText value="Level" />
</f:facet>
<h:selectOneListbox id="level" value="#{entitiy.level}" size="1"
valueChangeListener="#{someHandler.changeLevel}"
onchange="submit()">
<f:selectItem itemValue="-" itemLabel="-" />
<f:selectItem itemValue="ALL" itemLabel="ALL" />
(and so on)
</h:selectOneListbox>
</h:column>
<h:column>
<f:facet name="header">
<t:outputText value="Name" />
</f:facet>
<h:outputText value="#{entitiy.name}" />
</h:column>
</h:dataTable>
The valueChangeListener looks like this:
public void changeLevel(ValueChangeEvent event) {
String newLevel = (String) event.getNewValue();
Logger logger = (Logger) dataTable.getRowData();
logger.setLevel(Level.toLevel(newLevel));
}
(dataTable is an HtmlDataTable object.)
However, the event object is always the same - no matter which row the selectOneListbox was in. (It seems always the logger in the first row). The Logger object I get is also not the one I want.
Any ideas? Thank you!
And anothers questions? Is the entitiy.setLevel() method called even though I have a valueChangeListener? I use entitiy.level because I want to show the chosen level as a default for those entity.
Thank you!
There are two potential problems here:
First, the onchange="submit()" submits the entire form. The valueChangeListener will be invoked on all input elements of which the submitted value differs from the initial value.
You need to preset the value behind #{entitiy.level} with the same value as the default menu option. E.g. in the constructor.
public Entity() {
level = "-";
}
Or, better, make the default value null.
<f:selectItem itemValue="#{null}" itemLabel="-" />
so that the valueChangeListener won't be invoked when the particular menu is not changed.
Or, when you are already on JSF 2.x (please always mention exact JSF impl/version in your JSF questions), you can use <f:ajax> tag for this without the need for a valueChangeListener with a hacky onchange="submit()".
Second, you need to ensure that the datatable value #{someHandler.entities} returns exactly the same list during the submit as it was during the initial request. So, do the data loading in the bean (post)constructor. In JSF 2.x you'd like to put the bean in the view scope as well.
Unrelated to the concrete problem, you can also just use <h:selectOneMenu> instead of a <h:selectOneListbox size="1">.
Related
Consider a simple h:outputText component:
<h:outputText value="#{myBean.myValue}"/>
How can I lazy load that value after the page has been rendered, and display custom 'ajax loading' icon instead of the value while this is being done?
I am using PrimeFaces 3.5 in my project so any PF-specific implementation will be welcome.
A suggest to do this by calling remoteCommand after on page load (it is done by setting autoRun attribute to true) and update your outputText.
private String myValue;
// getter and setter
public void initMyValue() {
// init myValue
}
On page you should have ajaxStatus component for viewing loading image, and your outputText. Also there should be p:remoteCommand component:
<p:ajaxStatus style="width:16px;height:16px;" id="ajaxStatusPanel">
<f:facet name="start">
<h:graphicImage value="ajaxloading.gif" />
</f:facet>
<f:facet name="complete">
<h:outputText value="" />
</f:facet>
</p:ajaxStatus>
<h:outputText id="myText" value="#{myBean.myValue}"/>
<p:remoteCommand autoRun="true" actionListener="#{myBean.initMyValue}" update="myText"/>
EDIT: I supposed that you want to lazy load value of outputText because it contains some long running calculations, but if you want to completely deffer rendering of outputText first add boolean property in your backing bean, and set this property to true at the end of initMyValue method:
private boolean loaded;
// getter and setter
public void initMyValue() {
// init myValue
loaded = true;
}
on the page reorganize it as follows:
<h:panelGroup id="myPanel" layout="block">
<h:graphicImage value="ajaxloading.gif" rendered="#{!myBean.loaded}"/>
<h:outputText value="#{myBean.myValue}" rendered="#{myBean.loaded}"/>
</h:panelGroup>
<p:remoteCommand autoRun="true" actionListener="#{myBean.initMyValue}" update="myPanel"/>
You can use a BlockUI to conditionally block the component while it loads.
Define a preRenderComponent event on the <h:outputText/>
<h:outputText id="myText">
<f:event name="preRenderComponent" id="started"/>
</h:outputText>
Define a <p:blockUI/> with the id of the event as the trigger
<p:blockUI block="myText" trigger="started" />
You can customize the blockui to display an image or whatever.
A word of caution: I presume you require this because you're doing some heavy lifting in the getter of that component. Know that the getter will be called several times in the lifecycle of that page. So hiding the fact that the operation is taking a long time will not change the fact. A better design would be to preload and cache the value for that component in a durable scope, rather than the theatrics of a "loading" throbber.
This is how I ended up implementing it:
<h:panelGroup id="loginLocation">
<p:graphicImage library="assets" name="small-kit-loader.gif" width="16" height="16" rendered="#{empty mybean.lastLoginLocation}"></p:graphicImage>
<h:outputText value="#{myBean.lastLoginLocation}" rendered="#{!empty myBean.lastLoginLocation}"/>
</h:panelGroup>
<p:remoteCommand global="false" actionListener="#{actionBean.getUserLoginLocation(myBean.selectedUser)}" name="refreshLoginLocation" id="rc1" autoRun="true" update="loginLocation" process="#this"></p:remoteCommand>
Personally I am not entirely happy with this implementation:
lazy loading state is stored server-side, not client-side where it should be
I have to implement separate method on my backing bean (getUserLoginLocation) to retrieve the value, and explicitly store it in another property (lastLoginLocation). It would have been much cleaner just to have a single getter that is lazy-called after rendering the page in browser
Not easily reusable - depends on backing bean 'loaded' flag (#{empty myBean.lastLoginLocation} in this case), and requires action listener to actually set the value. Any composite component based on this approach would also depend on specific code in backing bean.
Any recommendations on how to improve this code are welcome! :)
I have a page with several h:selectOneMenu or p:selectOneMenu and I want to use the same page for editing and adding data.
When I will edit data I need f:selectItem. I know that this component doesn't have attribute rendered. And I read that I can use <c:if>.
Ok. For example, if I write
<p:selectOneMenu rendered="#{not empty bean.id}"
value="#{bean.selectedId}">
<c:if test="${editableBean != null}">
<f:selectItem itemLable="#{editableBean.name} itemValue=#{editableBean.id} />
</c:if>
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
Will it works without any problems in primefaces and with ajax listeners?
The easy solution (but with poor performance) will be to have a boolean editMode attribute in your managed bean to enable/disable the components. Basic example:
<p:selectOneMenu rendered="#{not empty bean.id}" disabled="#{bean.editMode}"
value="#{bean.selectedId}">
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
In your bean
#ManagedBean
#ViewScoped
public class Bean {
private int id;
private boolean editMode;
//other attributes...
//getters and setters...
#PostConstruct
public void init() {
//a way to know if the bean it's in edit mode
editMode = (id != 0);
}
}
This solution will have poor performance because every <p:selectOneMenu> will have to load all the data and then select the actual value, but it will do what you want. Another option will be to use this attribute for the rendered property of <p:selectOneMenu> and for an <h:inputText disabled="true" readonly="true" /> (or maybe <h:outputText />). Another basic sample:
<p:selectOneMenu rendered="#{not empty bean.id && not bean.editMode}"
value="#{bean.selectedId}">
<f:selectItems value="#{bean.listItems}" var="item"
itemLabel="#{item.name}" itemValue="#{item.id}"/>
</p:selectOneMenu>
<h:inputText rendered="#{bean.editMode}" value="{bean.selectedText}"
disabled="true" readonly="true" />
I have a list of users that I want to display in a JSF page using the primefaces p:datatable. I have no exception, but I can't display the data that a have into the list,even the column titles are not shown, in fact, I user the 'getUserListe()'method to get the data from the database calling my service layer, to be sure of that, I added a sysprint() of some userDomain properties (my model class), when I access to the URL, I want to populate my datatable with the data from the UserListe, so I call it by value="#{userMB.userListe}", the problem is that the list is not displayed. but when the page is charged, with means that getUserListe() is called, in my console, the proprties are printed successfullty, someone has any idea about this ?? :
a part of my managed bean :
#ManagedBean(name="userMB")
#RequestScoped
public class UserManagedBean implements Serializable {
private List<UserDomain> userListe ;
public List<UserDomain> getUserListe() {
this.userListe = new ArrayList<UserDomain>();
this.userListe.addAll(getUserServicee().getAll());
for(UserDomain u : userListe)
System.out.println(u.getFirstName());
return userListe;
}
public void setUserListe(List<UserDomain> userListe) {
this.userListe = userListe;
}
part of my jsf.xhtml file:
<h:form>
<h:outputText value="USER's rights list: "></h:outputText>
<p:dataTable id="userss" var="userr" value="#{userMB.userListe}" style="width: 10%">
<h:column>
<f:facet name="header"><h:outputText value="FirstName" /></f:facet>
<h:outputText value="#{userr.firstName}" />
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="LastName" /></f:facet>
<h:outputText value="#{userr.lastName}" />
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="Action" /></f:facet>
<h:commandLink value="Edit" action="#{userMB.editAction(userr)}" />
</h:column>
</p:dataTable>
<h:commandButton value="OK" action="#{userMB.userProp}" />
</h:form>
Note that my user has all the getters and setters of his properties setted correctly,
thank you for help
I did as #luiggi said in his comment by changing the <h:column> by the <p:column> and it's work fine, so thank you.
I also changed my managed bean from an #RequestScoped to an #ViewScoped one.
the other thing that I want to add and share with you , is that I did some research after reading the comments of this post, so I found an other solution that seems better than others to load the list only one time, It's by attaching a listener to a system event, so in my jsf file I add
<f:view>
<f:event type="preRenderView" listener="#{userMB.loadUserListe}" />
...
the loadUserListe() method in my bean call the business logic code which instantiate the list of users.
Of course, the jsf page is enclosed in an f:view. Note also that the use of this listener requires at least the JSF 2.0
thank you all for your help, and excuse me for my english, I hope this response helps others
I am calling valueChangeListener on a <h:selectBooleanCheckbox> which is inside a dataTable. and that dataTable is again inside another(outer) dataTable. In the valueChangeListener method I want the instance object of outer dataTable. Is there any way to get the object of outer dataTable instance?
EX:
<h:panelGroup id="panelId">
<p:dataTable id="outerDatatable"
var="supplier"
value="bean.supplierList">
<p:column>
<f:facet name="header">
<h:outputText value="Suppliers" />
</f:facet>
<h:outputText value="#{supplier.name}" />
</p:column>
<p:column>
<p:dataTable id="innerDataTable"
var="supplierAccount"
value="supplier.supplierAccountList">
<p:column>
<h:selectBooleanCheckbox id="booleanBoxId"
value="#{supplierAccount.supported}"
valueChangeListener="#bean.checkBoxListener}"
immediate="true"
onchange="this.form.submit();"/>
</p:column>
</p:dataTable>
</p:column>
</p:dataTable>
</h:panelGroup>
I found the following solution : I used <p:ajax> listener instead of valueChangeListener, and I could pass 'supplier' object as well as supplierAccount object to this listener method. We can pass any number of custom objects to <p:ajax> listener.
<p:column>
<h:selectBooleanCheckbox id="booleanBoxId"
value="#{supplierAccount.supported}"
immediate="true">
</h:selectBooleanCheckbox>
<p:ajax listener="#{bean.myListenerMethod(supplier,supplierAccount)}"
update=":formName:panelId"/>
</p:column>
In this particular case, you could get it by evaluating the #{supplier} programmatically:
public void checkBoxListener(ValueChangeEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
Supplier supplier = context.getApplication().evaluateExpressionGet(context, "#{supplier}", Supplier.class);
// ...
}
However, this is plain ugly, you're synchronously submitting the entire form by onchange="submit()". I recommend to throw in some ajax for that.
<h:selectBooleanCheckbox value="#{supplierAccount.supported}">
<f:ajax listener="#{bean.checkBoxListener}" render="???" />
</h:selectBooleanCheckbox>
(the render attribute is up to you)
with
public void checkBoxListener(AjaxBehavior event) {
Boolean value = (Boolean) ((UIInput) event.getComponent()).getValue();
FacesContext context = FacesContext.getCurrentInstance();
Supplier supplier = context.getApplication().evaluateExpressionGet(context, "#{supplier}", Supplier.class);
// ...
}
Or if your environment supports EL 2.2 and thus specifying method arguments in EL:
<h:selectBooleanCheckbox value="#{supplierAccount.supported}">
<f:ajax listener="#{bean.checkBoxListener(component, supplier)}" render="???" />
</h:selectBooleanCheckbox>
public void checkBoxListener(UISelectBoolean checkbox, Supplier supplier) {
boolean selected = checkbox.isSelected();
// ...
}
See also:
When to use valueChangeListener or f:ajax listener?
Unrelated to the concrete problem, as to using onchange="submit()", it may be useful to know that onchange doesn't work as expected for checkboxes in IE6/7. It get only fired on every 2nd click. You rather want to use onclick="submit()" instead.
I see that you forgot a brace ({) just before bean:
valueChangeListener="#{bean.checkBoxListener}" immediate="true"
Also, since you're using Primefaces, you could use it's components(that if you use version 3): http://www.primefaces.org/showcase-labs/ui/selectBooleanCheckbox.jsf
It isn't necessary to use outputText if you use jsf 2:
<f:facet name="header">
Suppliers
</f:facet>
Also it isn't necessary to use f:facet because the column component has an attribute called headerText:
<p:column headerText="Suppliers">
#{supplier.name}"
</p:column>
It's a lot simpler that way, isn't it?
PS: What's this? value="supplier.supplierAccountList" No #{ }?
I have a JSF datatable with a bunch of rows, with each row having a selectOneMenu inside of it like this:
<h:form
<h:dataTable id="myTable"
binding="#{myBean.dataTable}"
value="#{myBean.dataTableRows}" var="row"
first="0" rows="0" dir="LTR" frame="hsides" rules="all">
<h:column>
<f:facet name="header">
<h:outputText value="Sample Name" />
</f:facet>
<h:outputText value="#{row.sampleName}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Role" />
</f:facet>
<h:selectOneMenu value="#{row.role}"
id="roleInput">
<f:selectItems value="#{myBean.allRoles}" />
</h:selectOneMenu>
</h:column>
</h:dataTable>
<h:commandButton value="Save" action="#{myBean.save}" />
</h:form>
However, I can't seem to figure out how get the selected role out of each row in the save method. In other words, I want to save each row's value. I saw this article, which explains how to save an input text box:
http://balusc.blogspot.com/2006/06/using-datatables.html#EditableDatatable
but it doesn't seem to apply to the h:selectOneMenu case. Does anyone have any example code that does this?
Thanks!
I see your table has binding to your bean. In your bean you can use the getDataTable() method and access it. Java doc says:
public Object getRowData()
Return the data object representing the data for the currently selected row index, if any.
So if you do your code like:
List<String> selectedRowData = (List<String>) getDataTable().getRowData()
You can then access all the fields the user has chosen. Im using this in my own project and its working. The only difference is that Im casting to my own type instead of List<String>
There are no obvious errors in the form - if your save method is not being invoked, try adding a messages tag to your form to help track down the source of the problem. It would help if you posted a sample bean that reproduces the problem and state the JSF implementation and version you are using.