AAron
AAron

Reputation: 438

PrimeFaces DataTable rendering list wrong after listener sort (bug?)

DataTable does refresh, but not rendering list correctly. (JSF 2.2, Mojarra 2.2.0, PrimeFaces 5.2)

I have a list of dates in a PrimeFaces DataTable that I'm reordering each time the user adds or edits the date. This almost works perfectly. Please help me see what I'm doing wrong, or let me know I've found a bug. (Note, its not the update target, since I tried using update="@all" which didn't change anything.)

Try this with my code below:

1) Add 11/21/2015 then add 11/20/2015. Notice it reordered them onAdd.

enter image description here

Console:

onAdd, after
DataTableEntry[Date:Fri Nov 20 00:00:00 EST 2015]
DataTableEntry[Date:Sat Nov 21 00:00:00 EST 2015]

2) Then edit 11/21/2015 changing it to 11/19/2015. Notice the edited one went away and now there's two 20's. Bug?!?

enter image description here

Console:

onRowEdit, before
DataTableEntry[Date:Fri Nov 20 00:00:00 EST 2015]
DataTableEntry[Date:Thu Nov 19 00:00:00 EST 2015]
onRowEdit, after
DataTableEntry[Date:Thu Nov 19 00:00:00 EST 2015]
DataTableEntry[Date:Fri Nov 20 00:00:00 EST 2015]

3) Then hit my "Refresh page" commandButton. It now displays correctly.

enter image description here

Adding dates never has a problem regardless of its before/between/after positions. I found that you can edit the date, and as long as it doesn't actually change the order, it works fine. The moment the order is changed, the edited one disappears and one next to it gets visually duplicated into its place.

datatablesort.xhtml

<!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:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    >
<h:head>
    <title><ui:insert name="title">DataTable</ui:insert></title>
</h:head>

<h:body>
    <h:form id="modelerForm">
        <p:commandButton value="Refresh page" ajax="false"/>

        <h:outputLabel for="date" value="Date"/>
        <h:panelGroup>
            <p:calendar id="date" pattern="MM/dd/yyyy" placeholder="mm/dd/yyyy" mask="true" showOn="button" navigator="true"/>
            <h:commandButton value="Add">
                <f:ajax event="action" listener="#{dataTableBean.onAdd}" execute="date" render="entries"/>
            </h:commandButton>
        </h:panelGroup>

        <p:dataTable id="entries" value="#{dataTableBean.entries}" var="entry" emptyMessage="No dates added" editable="true" tableStyle="width:auto">
            <p:ajax event="rowEdit" listener="#{dataTableBean.onRowEdit}" update="entries"/>
            <p:column headerText="Dates">
                <p:cellEditor>
                    <f:facet name="output">
                        <h:outputText value="#{entry.date}">
                            <f:convertDateTime pattern="MM/dd/yyyy" />
                        </h:outputText>
                    </f:facet>
                    <f:facet name="input">
                        <p:calendar id="date" value="#{entry.date}" pattern="MM/dd/yyyy" placeholder="mm/dd/yyyy" mask="true" showOn="button" navigator="true"/>
                    </f:facet>
                </p:cellEditor>
            </p:column>
            <p:column>
                <p:rowEditor/>
            </p:column>
            <p:column>
                <p:commandLink title="Remove date">
                    <h:outputText value="" styleClass="ui-icon ui-icon-trash"/>
                    <f:ajax event="click" listener="#{dataTableBean.entries.remove(entry)}" render="entries"/>
                </p:commandLink>
            </p:column>
        </p:dataTable>
    </h:form>
</h:body>
</html>

DataTableBean.java

package com.aadhoc.test;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.event.AjaxBehaviorEvent;

import org.primefaces.component.datatable.DataTable;
import org.primefaces.event.RowEditEvent;

@ManagedBean(name="dataTableBean")
@SessionScoped
public class DataTableBean {

    private List<DataTableEntry> entries = new ArrayList<>();
    public List<DataTableEntry> getEntries() {
        return entries;
    }
    public void setEntries(List<DataTableEntry> entries) {
        this.entries = entries;
    }   

    public void onAdd(AjaxBehaviorEvent event) {
        DumpList("onAdd, before");

        UIComponent component = event.getComponent();
        UIComponent dateComp = component.findComponent("date");
        Date date = (Date) ((UIInput)dateComp).getValue();
        DataTableEntry entry = new DataTableEntry();
        entry.setDate(date);
        getEntries().add(entry);

        sortEntries(getEntries());
        DumpList("onAdd, after");
    }

    public void onRowEdit(RowEditEvent event) {
        DumpList("onRowEdit, before");

        // Get entries via DataTable#getValue because in real code
        // I don't have direct access to entries.
        DataTable dt = (DataTable) event.getSource();
        @SuppressWarnings("unchecked")
        List<DataTableEntry> entries = (List<DataTableEntry>) dt.getValue();
        sortEntries(entries);

        DumpList("onRowEdit, after");
    }

    private void DumpList(String msg) {
        System.out.println(msg);
        for (DataTableEntry entry : entries) {
            System.out.println(entry);
        }
    }

    private <T> void sortEntries(List<DataTableEntry> entries) {
        entries.sort(new Comparator<DataTableEntry>() {
            @Override
            public int compare(DataTableEntry entry1, DataTableEntry entry2) {
                return entry1.getDate().compareTo(entry2.getDate());
            }
        });
    }
}

DataTableEntry.java

package com.aadhoc.test;

import java.io.Serializable;
import java.util.Date;

public class DataTableEntry implements Serializable {
    private static final long serialVersionUID = -2513940455250513641L;
    private Date date;
    public Date getDate() {
        return date;
    }
    public void setDate(Date date) {
        this.date = date;
    }

    public String toString() {
        return getClass().getSimpleName()+"[Date:"+getDate()+"]";
    }
}

Upvotes: 2

Views: 2413

Answers (1)

fdelsert
fdelsert

Reputation: 808

I encountered the same problem.

Workaround : define the widgetVar attribute in the <p:dataTable widgetVar="datatableVar" ...> and add this line at the end of your onRowEdit function :

RequestContext.getCurrentInstance().execute("PF('datatableVar').filter()");

This will force the rendering of the entire datatable.

Upvotes: 2

Related Questions