How to use rich:dataTable with row selection without binding - jsf

I'm currently migrating a legacy project from JSF 1 (with RichFaces 3) to JSF 2 (with RichFaces 4). The project has several rich:dataTables with checkboxes next to the items. At the bottom of the page, there are a few action buttons to so something with all selected items (add to favorites, export as PDF, ...).
The checkboxes are the reason why the rich:dataTables have a binding to the corresponding UI beans. But most of the UI beans are session-scoped. In between, I've learned that binding in combination with session-scope is not a good idea. And indeed, I'm having lots of "duplicate ID" errors that go away if I change the scope to "request". But it's not that easy. These beens have some state that needs to be preserved between requests and an elaborated state initialization and cleanup logic.
I think I have two options:
change the beans to request-scope, do some massive refactoring by extracting the state into some new "stateObjects" to be saved in a new "sessionBean", and completely rewrite the state logic
remove the bindings, but how can I determine which entries of the rich:dataTable have their checkboxes selected?
Curiosly, the session-scope and bindings worked together very well with JSF 1. And the decision for making the beans session-scoped was made a long time before I took over the project.

You can remove binding and change actions (bean scope changing not needed).
1) Simple case: data table with list of items and after row selection detail for selected item is displayed
Following example uses Javascript (storing item ID in bean and calling action for presenting data related to this ID):
<a4j:jsFunction name="selectRow" action="#{locationAction.showSelectedLocation}">
<a4j:param name="locationId" assignTo="#{locationAction.selectedLocationId}" />
</a4j:jsFunction>
<rich:dataTable id="locationTable"
value="#{locationAction.locationList}" var="location"
rows="#{referenceData.recordsPerPage}"
onrowclick="selectRow('#{location.locationId}')">
...
</rich:dataTable>
2) Case with multiple check boxes and action button:
<rich:dataTable id="productTable" var="res"
value="#{productPicker.pickerList}">
...
<rich:column>
<h:selectBooleanCheckbox id="selectChck" value="#{res.selected}" />
</rich:column>
...
</rich:dataTable>
<a4j:commandButton value="#{msg.detachSelected}"
action="#{productPicker.deleteSelectedFromPickerList}"
render="picker" />
where item list is private List<PickerItem> pickerList; and item is from
public class PickerItem {
private Long id; // with getter and setter
// private Record item; // with getter and setter
private boolean selected; // with getter and setter
}
In my code there are equals and hashCode methods as pat of PickerItem class, but it should works without it too.

Related

How to use component binding in JSF right ? (request-scoped component in session scoped bean)

Mojara 2.1.21
I've updated my question based on comments. I have two situation where a component is bound to server session bean. (Additional links with information: Binding attribute causes duplicate component ID found in the view and https://stackoverflow.com/a/12512672/2692917)
Version 1:
single.xhtml:
<h:outputText value=... binding="#{mysessionbean.out}" />
java:
#SessionScoped #Named public class Mysessionbean {
UIOutput out;
//getter and setter ....
}
Version 2:
template.xhtml:
<h:outputText value=... binding="#{mysessionbean.out}"
view1.xhtml:
<ui:composition template="template.xhtml" />
view2.xhtml:
<ui:composition template="template.xhtml" />
java:
#SessionScoped #Named public class Mysessionbean {
UIOutput out;
//getter and setter ....
}
Version 1 is ok. (At least I've not encounter any errors so far). But in version 2 the duplicate id error is occured if I navigate from one page to another. Why does it happen ?
Is it safe to use (request-scoped) component (in version 1) with session scoped binding ?
Are there another use cases to consider ?
Edit:
Functional requirement 1:
I want to use Primefaces datatable in a view. I need some info from this datatable. (Such as selected row or row index). So binding the datatable helps me to retrieve this info.
Functional requirement 2:
Components binding in composite components. They will be bound to session scoped bean. (And used mainly on one page, but what if I used it on another page ?
Requirements 3
The situation as in "Version 2". Template with primefaces menu and session scoped binding. For this I've used the EL-Binding.
In JSF 2.x, unless you want to manipulate components programmatically (which is at its own also rather fishy), there is no sensible real world use case to bind components to a backing bean. For sure not if they are further not been used in the backing bean itself, or if it are solely their attributes which are been flattened out.
As to the functional requirement of getting the current row of the data table, there are much better ways listed here, How can I pass selected row to commandLink inside dataTable?, for example if your environment supports EL 2.2:
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<h:commandLink value="Foo" action="#{bean.foo(item)}" />
The two last requirements are totally unclear. At least, if you're doing something like:
<x:someComponent binding="#{bean.someComponent}" />
with in bean
someComponent.setSomeAttribute(someAttribute);
someComponent.setOtherAttribute(otherAttribute);
then you should instead be doing
<x:someComponent someAttribute="#{bean.someAttribute}" otherAttribute="#{bean.otherAttribute}" />
Or, if you intend to be able to use the component somewhere else in the view like so
<h:inputText ... required="#{not empty param[bean.save.clientId]}" />
...
<h:commandButton binding="#{bean.save}" ... />
and the instance is further nowhere been used in the bean, then just get rid of the unnecessary property altogether:
<h:inputText ... required="#{not empty param[save.clientId]}" />
...
<h:commandButton binding="#{save}" ... />
If there is really, really no way for some unclear reason, then split all request scoped properties of the session scoped bean out into a separate request scoped bean which you in turn bind to form actions. The session scoped one can just be injected as a #ManagedProperty of the request scoped one.
See also:
Binding attribute causes duplicate component ID found in the view
How does the 'binding' attribute work in JSF? When and how should it be used?
We ran into a similar problem and I just want to share our solution:
Problem:
In a view there was a (extended largely customized) datatable.
<x:dataTable binding="#{bean.someSomeDataTable}" />
After navigating to another page and back we wanted the datatable to have the exact same state. Previously we solved that by binding the datatable to to backing bean. This worked fine with JSPs. With Facelets we could not do that (Duplicate ID errors). So we used the binding, but only saved/restored the state of the datatable component.
public HtmlDataTable getSomeDataTable()
{
HtmlDataTable htmlDataTable = new HtmlDataTable();
if (tableState != null)
htmlDataTable.restoreState(FacesContext.getCurrentInstance(), tableState);
return htmlDataTable;
}
public void setSomeDataTable(HtmlDataTable table)
{
tableState = table.saveState(FacesContext.getCurrentInstance());
}

#ViewScoped, f:setPropertyActionListener only works once

I have 2 managedBeans: the first one (RequestScoped) sends a parameter to the second one (ViewScoped) through a h:commandLink. Both managedBeans are in the same page, but I use them in different tabs from a rich:TabPanel:
#ManagedBean
#RequestScoped
public class TheRequestScopedManagedBean {
private String number
...
#ManagedBean
#ViewScoped
public class TheViewScopedManagedBean {
private String number;
...
And here's the view, wich uses a rich:dataTable:
(The action method is only for showing the second tab from a rich:tabPanel).
// ... another dataTable's columns
<rich:column>
<f:facet name="header">Number</f:facet>
<b>
<a4j:commandLink value="#{theRequestScopedManagedBean.number}"
render="someRichPanel" action="#{anotherBean.showSecondTab}" immediate="true">
<f:setPropertyActionListener target="#{theViewScopedManagedBean.number}" value="#{theRequestScopedManagedBean.number}" />
</a4j:commandLink>
</b>
</rich:column>
The problem here is that theViewScopedBean shows the value only the first time, and when I try to pass it again, it shows its default value (null).
I've seen several questions on this website. But I really don't know what to do in this case.
action="#{anotherBean.showSecondTab}" is going to result in a navigation case firing. This will in turn result in the views and request scoped beans being destroyed and recreated (as is the expected behaviour for a view scoped bean).
If you're using EL2.2, you could easily just pass the value directly into a method in the view scoped bean. Let's assume your viewscoped bean has a method takeValue, you can pass the parameter directly into the method as in:
<a4j:commandLink value="#{theRequestScopedManagedBean.number}" render="someRichPanel" action="#{theViewScopedBean.takeValue(theRequestScopedManagedBean.number)}" immediate="true"/>
There are still cleaner ways to transmit data between pages.
Communication in JSF2.0

Get row number in p:dataTable of dynamic element

I'm curious about how to get the row number of an element inside the <p:dataTable>.
<p:dataTable id="userDataTable" value="#{bean.rows}" rowIndexVar="rowIndex">
<p:column headerText="RowCounter">
<p:commandLink id="row#{rowIndex+1}" actionListener="#{bean.getRows}">
<h:outputText value="Show Row #{rowIndex+1}" />
</p:commandLink>
</p:column>
</p:dataTable>
Bean:
public void getRows(ActionEvent ae) {
System.out.println(ae.getComponent().getId().toString());
}
Always prints row1, no matter which <p:commandLink> is clicked. What am I missing?
As to your concrete problem, the id attribtue of a JSF component is evaluated during view build time. However, the #{rowIndex} is only set during view render time. Thus, at the moment the id attribute is evaluated, the #{rowIndex} is nowhere been set and defaults to 0. This problem has essentially exactly the same grounds as already answered and explained here: JSTL in JSF2 Facelets... makes sense? Note thus that there's only one <p:commandLink> component, not multiple. It's just that it's been reused multiple times during generating HTML (everytime with the same component ID!).
To fix it, just use id="row" instead. The dynamic ID makes no sense in this particular case. JSF would already automaticlaly prepend the row index (check generated HTML output to see it). I'm not sure why exactly you incorrectly thought that you need to manually specify the row index here, so it's hard to propose the right solution as there are chances that you actually don't need it at all. See also How can I pass selected row to commandLink inside dataTable?
For the case you really need the row index, here's how you could obtain it:
Integer rowIndex = (Integer) ae.getComponent().getNamingContainer().getAttributes().get("rowIndex");
The UIComponent#getNamingContainer() returns the closest naming container parent which is in your particular case the data table itself, in flavor or UIData which in turn has thus the rowIndex property. You can alternatively also do so, which is a bit more self documenting:
UICommand commandLink = (UICommand) ae.getComponent();
UIData dataTable = (UIData) commandLink.getNamingContainer();
Integer rowIndex = dataTable.getRowIndex();

JSF: Reload page after data change

Using a DataModel<MyObject>, I fill a data table according to this response
Now at the end of each line I have a delete button which calls a certain method of my bean. The data gets cleanly deleted. But as the data has been changed after the deletion, I'd like to reload the page in order to reflect the changes.
My attempt was to add a navigation rule in faces-config.xml:
overview
/overview.jsp
deletedsubscription
/overview.jsp
If I have <redirect /> or not, either way it's not reloading or doing anything else at all. The data is deleted, therefore the bean's method is actually called.
My button looks like this:
<h:dataTable border="1" value="#{overviewBean.overviewModel }" var="item" first="0">
<h:column id="column13">
<f:facet name="header">
<h:outputText value="#{messages.overviewDeleteItem }"></h:outputText>
</f:facet>
<h:commandButton action="#{overviewBean.deleteItem}" value="X"/>
</h:column>
</h:dataTable>
Setting the attribute type="submit" actually solves the problem. The pages gets reloaded.
My question now: is the submit really required? Doesn't JSF (Apache MyFaces) have some mechanism of using AJAX to eliminate the line just deleted?
Thanks for trying to help a JSF-newbie.
Just remove the item from the backing list of the datamodel. The changes will be reflected in the model.
Basically:
private List<MyObject> overviewList;
private DataModel overviewModel;
public OverviewBean() {
overviewList = overviewDAO.list();
overviewModel = new ListDataModel(overviewList);
}
public void deleteItem() {
MyObject myObject = (MyObject) overviewModel.getRowData();
overviewDAO.delete(myObject);
overviewList.remove(myObject); // See?
}
A redirect thereafter is not necessary. If the action method returns void (or null), it will go to the same page anyway.
Ajaxical capabilities have been introduced in JSF 2.0, but if I am not wrong, you're still on JSF 1.x. Your best bet is then adopting a 3rd party component library like Ajax4jsf (currently part of RichFaces), for the case that you consider this necessary.

Richfaces extendeddatatable sorting problem

I am developing a web app using JSF, RichFaces, EJB3, Hibernate and Seam.
I have an extended data table and showing a list (say userList) which has multi columns in it. Moreover, because of that this datatable is selectable, I want to keep the selected row indexes even if the sorting is changed by the user.
In other words of what I mean is that when the sorting of columns is changed, the order of row indexes is changed as well. Therefore, I want to invoke an action when the user clicks on sorting on each time. I tried many ways, but could nt find a solution to achieve it yet.
Do you have any idea about which listener or method is being called when sorting is clicked by the user in rich extendeddatatable? I cant understand what the point is in that...
Any help would be appreciated.
Many Thanks,
Baris
A code sample would have been nice, but it sounds like you have a separate list that contains the selected indices, and only update that list on a selection event.
Have you considered having the selection state live with the data object via a bean:
public DataBean {
private DataModel model;
private boolean selected;
//standard getters and setters omitted
}
JSF code:
<h:column>
<h:selectBooleanCheckbox value="dataBean.selected">
<a4j:support event="onclick" ajaxsingle="true" />
</h:selectBooleanCheckbox>
</h:column>
Tie your selection state to the model like this and sorting won't be an issue at all.

Resources