Primefaces - how to get the column of a CellEditEvent - java

I am using primefaces 4.
I am using an editable table and when I edit a cell, a listener method is called passing a CellEditEvent
Like this
public void onCellEdit(CellEditEvent event) {
/*
* The rowIndex here can be changed according to the sorting/filtering.
* FilteredData starts as null, but primefaces initializes it, so you
* don't have to check for NPE here
*/
int alteredRow = event.getRowIndex();
UIColumn o = event.getColumn();
System.out.println(this.filteredData.get(event.getRowIndex()).get(columns.get(0)));
}
So far, so good.
The event has a getRowIndex()
But it does not have a getColumnIndex().
Instead, it has a getColumn() method that returns a UIColumn object.
The problem is, while debugging, I could not find a way to get any column information (name, id, etc)
I can hack the column to have some unique ID like this
<p:ajax event="cellEdit" listener="#{myMB.onCellEdit}"/>
<c:forEach items="#{myMB.columns}" var="column" varStatus="loop">
<p:column id="col#{loop.index}" headerText="#{column}" sortBy="#{column}" filterBy="#{column}" filterMatchMode="contains"/>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{dataRow[column]}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{dataRow[column]}" />
</f:facet>
</p:cellEditor>
</p:column>
</c:forEach>
But still I can't find a way to retrieve the column id from the CellEditEvent
So, assuming that a cell is something that has a row and a column, I have to ask
How do I retrieve the column of an edited cell in a CellEditEvent?
ps. I feel I am missing something, because no one would create a cell event without providing the row and the column, right?
update - it seems I can get the ID like
org.primefaces.component.column.Column o = (org.primefaces.component.column.Column)event.getColumn();
still, this seems like a hack for me. I am still interested in more elegant solutions for this ;-)

You can get the column by referring back to the column header that you passed.
In bean you could do this:
public void onCellEdit(CellEditEvent event) {
int alteredRow = event.getRowIndex();
String column_name;
column_name=event.getColumn().getHeaderText();
// now you can use this to identify the column we are working on.
}
Using getColumnId() or getColumnKey() returns the column Id but with primefaces code added to it - making it difficult to work with.

If you use p:columns rather than p:column - which is also more readable - event.getColumn() returns a DynamicColumn that implements UIColumn. Then you can use:
((DynamicColumn)event.getColumn()).getIndex()
to determine the column index. There is also a column.isDynamic() to check validity.
Using the header would need further code and might be more inefficient.

I found only one way to do this. On the xhtml, add an id to the column:
<p:column id="myColumn">
...
</p:column>
In the backing bean,column.getColumnKey() will return a generated id that contains the id you set in the xhtml (something like j_idt30:j_idt32:0:contratoReal). Thus, you can do this:
public void onCellEdit(CellEditEvent event) {
if (event.getColumn().getColumnKey().endsWith("myColumn")
}

Related

Primefaces DataTable display only the last item

the Datatable is displaying only the last item added, it seems that every time i press the commandButton the method rewrites the list, what can I do to avoid that?
JSF
<p:commandButton action="#{productoBean.setPedidoActual()}" value="Agregar" update="dt"/>
</p:panelGrid>
<p:dataTable value="#{productoBean.pedidoActual}" var="pedi">
<p:column headerText="Nombre">
<h:outputText value="#{pedi.descripcion}"/>
</p:column>
</p:dataTable>
Bean
public void setPedidoActual() {
Producto pro = productoFacade.find(idProducto);
listPedidoActual.add(pro);
}
public List<Producto> getPedidoActual() {
return listPedidoActual;
}
Your setPedidoActual() method seems like to receive only one item based on the facade service find(idProducto), is that correct? Have you checked what the remote service returns? Probably there is in fact just one record.
Beside that, try to rename your setter/getter from
getPedidoActual() / setPedidoActual()
into
getListPedidoActual() setListPedidoActual()
Now your method names are consistent with your attribute.
EDIT: What happened to the panelGrid tag? Have you purposely omitted or where does it start?

JSF: rich datatable filtering, dynamic columns, and passing arguments

I'm working on a rich:datatable on a JSF page. The table can get pretty big and is paged with a rich:datascroller. Most of the columns are hardwired and will always be there, but then there are some optional columns based on additional values that need to be generated for each potential additional value. I've been able to make this happen easily enough. However, I'm running into a problem with filtering.
I'm using a filter on each column. It's placed in the header with the column label and sorting function. That much is working fine on each column, but I'm hitting a snag on filtering due to the way filtermethod works by default. Here's a quick example:
<rich:datatable id="thetable" value=#{backingBean.stuff} var="b">
<!-- First column, standard filter method, works just fine -->
<rich:column sortBy="#{b.field1}" filterMethod="#{filterBean.filterField1}">
<f:facet name="header">
<ui:fragment>
<h:outputText value="Field 1" />
<h:inputText value="#{filterBean.filterMap['Field1']}" />
</ui:fragment>
</f:facet>
#{b.field1}
</rich:column>
<c:forEach items="#{backingBean.extraStuff}" var="e">
<rich:column sortBy="#{b.getExtra(e)}" filterMethod="???">
<f:facet name="header">
<ui:fragment>
<h:outputText value="#{b.getExtra(e).description}" />
<h:inputText value="#{filterBean.filterMap['b.getExtra(e).code']}" />
</ui:fragment>
</f:facet>
#{b.getExtra(e).description}
</rich:column>
</rich:datatable>
The ??? will be covered shortly. As for the filter bean:
public class FilterBean {
public Map<String, String> filterMap = new HashMap<String, String>();
public boolean filterField1(Object current){
return ((BackingBean) current).contains(filterMap.get("Field1"));
}
}
It's fairly straightforward. The filter inputText binds to a preset string in the hashMap, which is retrieved in the method and used to filter so I don't need a separate field for every filter. This is working great, but I still need a separate method for each filter, which brings me to the ??? in the JSF code...
What I'd like to do is pass arguments to the filter method to account for the dynamic columns. In fact, I'd like to simplify the whole class with a single filter method and pass the mapped String in along with the field from the current object. However, this isn't working. I've tried:
filterMethod="#{filterBean.filterStuff(b, 'Field1')}"
but I wind up getting the filter string just fine, but null for the current object. I'm not sure what's going on. If I'm reading the dependencies in the project correctly, I'm using some pretty old versions of EL, JSF, JSP, etc, and I really have no way of changing that. The project does use Seam, though, and I've passed arguments successfully in EL before in this project. Is it just that EL 2.2 supports passing objects while older versions only supported primitives and Strings? Is there any way for me to make this happen or am I stuck without building a ton of extra stuff from the ground up?
Okay, looks like this might be possible with Seam, but it doesn't like iteration variables. I CAN pass the object if I refer to an index in the List from the backing bean, but that doesn't help as I have no way of telling it to search every row...
My use case is a bit different, but basically I had the same problem and found a working solution.
The use case: I have several XHTML-files with their backing-beans all offering a table with a list of entities of different types. In this table there are several columns for some attributes of the entities with the possibility to filter. Since the built-in filter does only a "starts-with" search and I need a more advanced one, I have to use the filterMethod.
But I did not want to mess up my backing-beans with hundreds of simple filter-methods doing all exactly the same (only with different attributes). So I was looking for a more generic way - and this is my approach:
In the backend, I created a new class named FilterMethodWrapper (for easier understanding I put it as nested static class here) and a method to access it:
import org.apache.commons.beanutils.PropertyUtils;
public class BackendBean
{
private String[] filterValues;
// initialize filterValues somewhere, add getter and setter
public static class FilterMethodWrapper
{
private final String fieldName;
private final String filterValue;
public FilterMethodWrapper(final String fieldName, String filterValue)
{
this.fieldName = fieldName;
this.filterValue = filterValue;
}
public boolean doFilter(Object current) throws ...
{
final String stringValue = (String) PropertyUtils.getSimpleProperty(current, fieldName);
// compare stringValue and filterValue somehow (e.g. contains)
// and return result
}
}
public FilterMethodWrapper getFilterMethodWrapper(String fieldName, int filterValueIndex)
{
return new FilterMethodWrapper(fieldName, getFilterValues()[filterValueIndex]);
}
}
And in the XHTMLs use it as follows:
<rich:column filterMethod="#{backendBean.getFilterMethodWrapper('username', 0).doFilter}" filterEvent="onkeyup" >
<f:facet name="header">
<h:panelGrid style="display:inline;">
<h:outputText value="Username"/>
<h:inputText value="#{backendBean.filterValues[0]}" />
</h:panelGrid>
</f:facet>
<h:outputText value="#{_item.username}" />
</rich:column>
Edit: I'm using JSF 1.2 and RichFaces 3.3.3.Final
Edit2: instead of writing the FilterMethodWrapper you could also use some Predicate-implementation and use the apply-method in the frontend (or you write your own Predicate-implementation according to this proposal which is more reusable than this FilterMethodWrapper)

Primefaces 3.4 dataexporter does not export values p:cellEditor

I'm using Primefaces 3.4 and trying to export a data-table with in-cell editing. It doesn't seem to work.
I have done the following:
Modified org.primefaces.component.export.Exporter line 143 and added this:
else if (component instanceof CellEditor) { // Handle in-cell editable datatables
return exportValue(context, ((CellEditor) component).getFacet("output"));
}
This is causing an extra row on top as well as an extra column to the right of the actual cells with data. With Excel files it's OK since they are not "visible" but PDF looks bad.
Is there a way to make PF 3.4 support this without changing the source code?
If the answer to the above is negative, can I make the PDF get generated without the additional row/column?
Is there a way to make PF 3.4 support this without changing the source code?
No. You've to provide your custom Exporter implementations/overrides. I've already reported this as issue 4013 several months ago. It not only mentions CellEditor, but also HtmlGraphicImage (we are using images to show boolean states, whose alt we'd like to show in PDF/XML/XLS/CSV reports).
All you can do is to vote for it so that it'll hopefully get more attention, or by re-asking it on PF forum.
If the answer to the above is negative, can I make the PDF get generated without the additional row/column?
You can hide a column from export by setting the exportable="false" attribute.
<p:column exportable="false">
Is there a way to make PF 3.4 support this without changing the source code?
Yes. There is a workaround
Make two copies of your column.
First one is for users and second one is for dataExporter.
By setting exportable="false" in first column, hide it from dataExporter.
By setting style="display: none" in second column, hide it from users.
By the way dataExporter does not support headerText so if you need your headerText exported you have to use old style <f:facet name="header">.
<p:column headerText="CLOSE DATE" exportable="false">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{sale.closedate}"/>
</f:facet>
<f:facet name="input">
<h:inputText value="#{sale.closedate}"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column style="display: none">
<f:facet name="header">
CLOSE DATE
</f:facet>
<h:outputText value="#{sale.closedate}"/>
</p:column>
But this workaround is plain ugly and makes your dataTable twice in size and hard to render.
As BalusC mentioned we need to rise awareness about this issue by voting or posting in their forum.
The only solution that I've found so far is to make your own class that extends Exporter and to override method protected String exportValue(FacesContext context, UIComponent component). You should only add
else if (component instanceof CellEditor) {
return exportValue(context, ((CellEditor) component).getFacet("output"));
}
the answer is... create a data table with cell editor....
and after create an other table with no cell editor but the same fields into a
the exporter should be reference to the second table, and it is! :D
greetings!

Radio and Checkbox in primefaces datatable

I have to put radio and chechbox controls in primefaces's 2.2.1 datatable but not as a selection mode controls. I want to bind that controls as a values for the backing bean.
For example:
A have a list of some configurations and in that list only one configuration can be active at one time (radiobox) but every single configuration can be active or not (selectbox).
Datatable doesn't have to be editable, because I will change values in another window. Only controls should be in the datatable.
Is that possible?
Yes, it is definitely possible I do this all the time in my datatables. However, you need to keep in mind that each row will require identification. You can do this with objects or parameters to your listener:
//Backing Bean
#ViewScoped
public class TestBean
{
private ArrayList<Element> elements;
TestBean(){...}
public ArrayList<Element> getElements(){...} //Initialize things here
public class Element
{
/* getter/setter and initialization assumed */
private boolean selected;
private String radioSelection = "one";
private String[] radioChoices = new String[]{"one", "two", "three"};
public void selectMe(ActionEvent evt)
{
System.out.println("Selected -> " + this);
}
}
}
//HTML
<p:dataTable value="testBean.elements" var="element">
<p:column>
<p:commandButton value="Select" actionListener="#{element.selectMe}"
</p:column>
<p:column>
<h:selectBooleanCheckbox value="#{element.selected}" />
</p:column>
<p:column>
<h:selectOneRadio value="#{element.radioSelection}">
<f:selectItems
value="#{element.radioChoices}"
var="item"
itemLabel="#{item}"
itemValue="#{item}"
/>
</h:selectOneRadio>
</p:column>
</p:dataTable>
You can (and might want to) use a parameter using the ID from the element or whatever your preferred pattern is. The key is remembering that each row has its own id and you're creating a set of elements, not a single element. That's where you're most likely to get bound up. Seeing as how each button is a first class citizen you should be able to whatever you want with it.
Edit:
I added a Radio/Checkbox example. It sounds like you're new to JSF so I'd advise reading up on the SelectItem class and run through how combo-boxes etc... work. The key to remember (as I stress above) is that you're rendering a lot of components so if you share a value it can easily lead to behavior you don't want. Encapsulate everything and you're usually in good shape.) You can also look at bindings as a possible solution. It all depends on where you'd rather write your code.

Loading a set of images with primefaces

I have the next code to load a set of images whose streams are in a datamodel called names. My problem is when I declare the var inside the p:datatable tag seems like has nothing. Any idea?
thx!
<p:dataTable value="#{controlador.names}" var="nombre" rendered="true">
<p:column rendered="true">
<h:outputText value="#{nombre.stream}"/>
<p:graphicImage value="#{nombre.stream}"/>
</p:column>
</p:dataTable>
The p:graphicImage uses another request so you need to pass an identifier to the managedBean like this.
<p:dataTable value="#{productManaged.products}" var="productIterated">
<p:column>
<f:facet name="header">
<h:outputText value="#{product.pic}"/>
</f:facet>
<p:graphicImage value="#{productManaged.dynamicProductImage}">
<f:param name="product_id" value="#{productIterated.id}"/>
</p:graphicImage>
</p:column>
</p:dataTable>
Another thing that you should take care is to return something in the StreamedContent or is gonna fail. Do something like this:
public StreamedContent getDynamicProductImage() {
String id = FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap().get("product_id");
if(id!=null && this.products!=null && !this.products.isEmpty()){
Integer productId = Integer.parseInt(id);
for(Product productTemp:this.products){
if(productTemp.getId().equals(productId)){
return new DefaultStreamedContent(
new ByteArrayInputStream(productTemp.getImage()),
productTemp.getMimeType());
}
}
}
return new DefaultStreamedContent(
new ByteArrayInputStream(this.products.get(0).getImage()),
this.products.get(0).getMimeType()); //if you return null here then it won't work!!! You have to return something.
}
or you can read this thread http://primefaces.prime.com.tr/forum/viewtopic.php?f=3&t=4163
After wasting hours of going the the process of implementing the many solutions I found for this issue (i.e. including a param or attribute), The only solution I managed to find that actually works can be found here: Serving Dynamic Content with PrettyFaces
Remove the <h:outputText>. You can read a stream only once. It cannot be re-read another time.
As to the p:graphicImage part, you need to feed it with a value of DefaultStreamedContent. Also see this blog entry.

Categories