38

I am having difficulty re-rendering a PrimeFaces Datatable once a cell has been edited. Changing the value in one cell may change entries in the other cells, hence the need to refresh the entire table.

Here's the JSF page:

<h:form id="testForm">
    <p:outputPanel id="testContainer">

        <p:dataTable id="testTable" value="#{tableBean.data}" var="entry" editable="true" editMode="cell">

            <p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />

            <p:column headerText="Col1">  
                <p:cellEditor>
                    <f:facet name="output"><h:outputText value="#{entry.col1}" /></f:facet>
                    <f:facet name="input"><p:inputText value="#{entry.col1}" /></f:facet>
                </p:cellEditor> 
            </p:column>

            <p:column headerText="Col2">
                <p:cellEditor>
                    <f:facet name="output"><h:outputText value="#{entry.col2}" /></f:facet>
                    <f:facet name="input"><p:inputText value="#{entry.col2}" /></f:facet>
                </p:cellEditor>  
            </p:column>

        </p:dataTable>

        <p:commandButton id="refreshButton" value="Redisplay" update="testContainer" />

    </p:outputPanel>                                    
</h:form>

And here's the backing bean:

@ManagedBean(name = "tableBean", eager = false)
@ViewScoped 
public class TableBean {

    public TableBean() {
        RowData entry = new RowData("a1", "b1");
        entries.add(entry);
        entry = new RowData("a2", "b2");
        entries.add(entry);
        entry = new RowData("a3", "b3");
        entries.add(entry);
    }

    public class RowData {

        private String col1;
        private String col2;

        public RowData(String col1, String col2) {
            this.col1 = col1;
            this.col2 = col2;
        }

        public String getCol1() {
            return col1;
        }

        public void setCol1(String col1) {
            this.col1 = col1;
        }

        public String getCol2() {
            return col2;
        }

        public void setCol2(String col2) {
            this.col2 = col2;
        }
    }

    private ArrayList<RowData> entries = new ArrayList<RowData>();

    public List<RowData> getData() {
        return entries;
    }

    public void onCellEdit(CellEditEvent event) {
        entries.get(event.getRowIndex()).setCol1("Dummy Col 1");
        entries.get(event.getRowIndex()).setCol2("Dummy Col 2");        
    }   
}

When including update=":testForm:testContainer" within the cellEdit AJAX event, changing a cell value deletes the datatable on screen and only renders the cell content (along with the button) -- I do not understand why this is. When the update attribute is not specified, the table remains on screen with the active cell updated, but none of the other cells are updated (as to be expected).

The desired behaviour can be achieved (in a non-automated way) by not specifying the update attribute within the AJAX cellEdit event and clicking the Redisplay button after editing a cell's value. How can I achieve this in an automated way, and why does the update attribute not work as I expect?

I am using PrimeFaces 4.0.

Jasper de Vries
  • 19,370
  • 6
  • 64
  • 102
Franzl
  • 719
  • 1
  • 6
  • 13

7 Answers7

62

The rowEdit and cellEdit events does by design inside the table not update/re-render anything else than the current row, even not when explicitly specified in update attribute. It's the consequence of PrimeFaces' a bit overzealous attempt to minimize the response size. This makes sense in most of the cases, but not in specifically your case. It's worth an issue report.

In the meanwhile, until they fix this behavior, your best bet is using <p:remoteCommand> to invoke the desired listener method and perform a full update of the table.

Rewrite

<p:dataTable ...>
    <p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />
    ...
</p:dataTable>

to

<p:remoteCommand name="onCellEdit" action="#{tableBean.onCellEdit}" update="testContainer" />
<p:dataTable ...>
    <p:ajax event="cellEdit" oncomplete="onCellEdit()" />
    ...
</p:dataTable>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • @BalusC I did as you guided but `Glassfish Server` prompts error that method `is NOT FOUND` as it is need to mention that this method as `Tim Long` said contains `CellEditEvent`param. I wanted to `update whole datatable` when **a cell** is edited. Can you help me please? – elgolondrino Dec 01 '14 at 06:44
  • Did anyone ever report this issue to Primefaces? We have Primefaces PRO and would be willing to escalate. – Greg Noe Feb 24 '15 at 21:40
  • 8
    Just a quick shout out and 'thank you'. I thought I was going crazy. For me, I left the ajax `cellEdit` listener in place, and then just moved the `update` attribute to the `remoteCommand` with no action in the `remoteCommand` component, essentially using solely to perform the `update` attribute I was using the ajax event for. – Doug May 05 '15 at 19:12
  • @BalusC This solution works ony if the editing of the cell is finished with the enter key. If a cell is in editor mode and another editable cell is clicked, then the cellEdit event is fired and the table updated but no longer editable! – ltlBeBoy Aug 03 '18 at 15:20
  • It's been more than 8 years now this issue still there. – Az.MaYo Apr 27 '22 at 09:59
28

The BaLusC solution has not worked directly for me. The onCellEdit needs a CellEditEvent as param. My workaround is as following:

<p:remoteCommand name="onCellEdit" update="testContainer" />
<p:dataTable ...>
    <p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" oncomplete="onCellEdit()" />
    ...
</p:dataTable>
Tim Long
  • 2,039
  • 1
  • 22
  • 25
  • You method works but only to edit one `cell`? How to fix this? – elgolondrino Dec 01 '14 at 06:40
  • In my case, BaLusC solution worked for inputText I have within an editable table, if the change is done in an autoComplete control within a cell, the whole table was not updated. With the remoteCommand solution, it works well. – Richard P. Aug 05 '15 at 09:06
  • The BaLusC solutions doesn't work for me too. This one works perfectly!! – Alessio Fiore Nov 04 '19 at 16:03
  • It's been more than 8 years now this issue still there. Your solution save my time thanks – Az.MaYo Apr 27 '22 at 09:59
6

If none of the solutions worked for you, this worked for me

<p:dataTable ... id="theId" widgetVar="theWidget" ...>
                <p:ajax event="rowEdit" listener="#{...}"
                        oncomplete="PF('theWidget').filter()"/> 
....

I'm calling the filter method on the PF widget on ajax complete, any method that does a "reload" of the table should work, I used filter because my table had column filters.

Lance Reid
  • 196
  • 2
  • 4
0

I tested your code. First I moved p:commandButton out of p:outputPanel. Here is modified code:

<h:form id="testForm">
            <p:outputPanel id="testContainer">

                <p:dataTable id="testTable" value="#{tableBean.data}" var="entry" editable="true" editMode="cell">

                    <p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" update=":testForm:testContainer" />

                    (...)

                </p:dataTable>
            </p:outputPanel>
            <p:commandButton id="refreshButton" value="Redisplay" update="testContainer" />
        </h:form>

I think this code doesn't work correctly. if you change anything in table, the p:ajax every time render full table. So, the program load basic data from TableBean constructor and deleted new data.

basic data

If I omit your p:ajax code there is not disapears any new data from screen. The refreshButton p:commandButton work correctly.

When including update=":testForm:testContainer" within the cellEdit AJAX event, changing a cell value deletes the datatable on screen and only renders the cell content (along with the button) -- I do not understand why this is.

I think it is bad design add update=":testForm:testContainer" to ajax, because it's update your outputPanel more than as exepted (first time work correctly, second time couldn't edit cell, because the program update to many times table).

I don't know what is your goal. If you want render table without a commandButton, then could you specify one javascript event or p:message and this disappear you can render table.

I think if you omit update in p:ajax or specify update of one p:message, and move p.commandButton out of testContainer your code start work correctly.

herry
  • 1,708
  • 3
  • 17
  • 30
0

After 5 years, this problem still exists. Unfortunately, while Baukes solution is extremly helpful and includes important insights it's still incomplete, as ltlBeBoy already pointed out in his comment. Subsequent edits without change lead to an inconsistent table state, where no more edits are possible. The reason is, that the oncomplete remote update comes after the edit mode of the new cell is already activated. So the edit mode of the new cell is destroyed by the update. However, the update can't be done in Ajax listener tableBean#onCellEdit, as this would display the table erroneously with one cell only.

The solution is, to execute the update in the remote commands listener and only, if a change happend. So, in tableBean you implement a programmatic update, a remote listener and a flag that indicates change:

public static void update(String id) {
   PrimeFaces pf = PrimeFaces.current(); //RequestContext.getCurrentInstance() for <PF 6.2
   if(pf.isAjaxRequest()) pf.ajax().update(id);
}

/** Whether onCellEdit changed the value */
boolean onCellEditChange;

public void onCellEditRemote() { 
    if(!onCellEditChange) update("testContainer");
}

public void onCellEdit(CellEditEvent event) {
    ...  onCellEditChange= /*Change happend*/ ...
}

The remote command has no update attribute any more:

<p:remoteCommand name="onCellEdit" actionListener="#{tabelBean.onCellEditRemote}"/>
Marc Dzaebel
  • 425
  • 4
  • 12
0

try using process

<p:remoteCommand name="onCellEdit" update="testContainer" process="@this" /> <p:dataTable ...> <p:ajax event="cellEdit" listener="#{tableBean.onCellEdit}" oncomplete="onCellEdit()" process="@this" /> ... </p:dataTable>

0

This is my first post ever in here. As ltlBeBoy mentioned, BalusC's solution works only if the cell editing is done with an enter key hit. None of the other suggestions listed in here worked for me. In my case, I just wanted to update a specific row (that has each column's average) in the table on cellEdit event. I'm posting this in case if someone out there is looking for a solution: I managed to achieve this by separating that row in a second datatable right below the main one. Here's the code:

<pf:ajax event="cellEdit"
         listener="#{newAgentMetricBacking.onCellEdit}"
         update="c21413" />

<pf:dataTable id="c21413"
              styleClass="noHeader"
              value="">
    <pf:column>
        <h:outputText value="#{bundle.TeamAverage}"/>
    </pf:column>
    <pf:columns columnIndexVar="j"
                value="#{newAgentMetricBacking.averageArray}"
                var="avg">
        <h:outputText value="#{newAgentMetricBacking. 
                               averageArray[j]}"/>
    </pf:columns>
</pf:dataTable>

Otherwise, as of today, I couldn't find a better solution for updating either the whole table or a specific row specifically upon success of cellEdit event. Cheers!

Mo-
  • 1
  • 1