Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reordering p: p:dataTable rows containing inputs

There is p:dataTable with p:inputText in a column:

<h:form id="form">
    <p:dataTable id="dataTable" value="#{rowReorder.dataList}" 
                 var="row" draggableRows="true" rowKey="#{row.id}">
        <p:ajax event="rowReorder" listener="#{rowReorder.reorder}" update="dataTable"/>

        <p:column>
            <f:facet name="header">
                <p:commandButton value="Add" actionListener="#{rowReorder.addData}" 
                                 update="dataTable" process="dataTable"/>
            </f:facet>
            <p:outputLabel value="#{row.id}"/>
        </p:column>
        <p:column>
            <p:inputText value="#{row.name}"/>
        </p:column>
    </p:dataTable>
</h:form>

Backing bean:

import org.omnifaces.cdi.ViewScoped;
import org.primefaces.event.ReorderEvent;
import javax.inject.Named;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;

@Named("rowReorder")
@ViewScoped
public class RowReorder implements Serializable {

    private List<Data> dataList = new LinkedList<>();

    public void addData() {
        Data data = new Data();
        data.setId(dataList.size() + 1);
        data.setName("Data " + data.getId());
        dataList.add(data);
    }

    public void reorder(ReorderEvent event) {

    }

    /**
     * Getters, Setters
     */

    public List<Data> getDataList() {
        return dataList;
    }
}

Data class:

public class Data implements Serializable {

    private Integer id;
    private String name;

    /**
     * Getters, Setters
     */

}

Sample datatable before reordering:

--------------
|id |  name  |
--------------
| 1 | Data 1 |
| 2 | Data 2 |
| 3 | Data 3 |
| 4 | Data 4 |
--------------

and after reordering (moving 1-st row to 3-rd):

--------------
|id |  name  |
--------------
| 2 | Data 1 |
| 3 | Data 2 |
| 1 | Data 3 |
| 4 | Data 4 |
--------------

I understand that it is happening 'cause of setting data from p:inputText's at UPDATE_MODEL phase. I tried to prevent processing of input fields by specifying process="@none" in p:ajax component, but it doesn't work. Have any idea how to make draggableRows and p:inputText friends?

like image 559
antonu17 Avatar asked Dec 08 '22 00:12

antonu17


1 Answers

First solution

I found a solution! It does not processing inputs (and actually does not submit it at all) with attributes process="@none" partialSubmit="true"

So complete p:ajax component looks like <p:ajax event="rowReorder" listener="#{rowReorder.reorder}" update="dataTable" process="@none" partialSubmit="true"/>


Second solution (if submitted data is needed)

Theory:

Lets check out what is happening on dragging row? We have ajax request forcing process="form:dataTable". On APPLY_REQUEST_VALUES phase DataTableRenderer tries to invoke decode of DraggableRowsFeature which, in turn, rotating list elements (list that specified as dataTable's value attribute). So on UPDATE_MODEL_VALUES phase we have a rotated list, and input components, which wants to submit their values to name fields of Data objects. But request parameters still contains old row indexes in input ids: they are form:dataTable:1:name = Data 2, form:dataTable:2:name = Data 3, form:dataTable:0:name = Data 1 (i added 3 rows, and moved first row to last). So here we getting what we got. In this way if we need data to be submitted on right places we have to prevent our list rotating before UPDATE_MODEL_VALUES is done, and perform this rotation later on INVOKE_APPLICATION phase, and render dataTable on that ajax request:

In DraggableRowsFeature.decode() we can see that Collections.rotate() is calling only when value is instance of List.

    if (value instanceof List) {
        List list = (List) value;

        if(toIndex >= fromIndex) {
            Collections.rotate(list.subList(fromIndex, toIndex + 1), -1);
        }
        else {
            Collections.rotate(list.subList(toIndex, fromIndex + 1), 1);
        }            
    } 
    else {
        LOGGER.info("Row reordering is only available for list backed datatables, use rowReorder ajax behavior with listener for manual handling of model update.");
    }     

Also there is DraggableRowsFeature.shouldDecode() method.

public boolean shouldDecode(FacesContext context, DataTable table) {
    return context.getExternalContext().getRequestParameterMap().containsKey(table.getClientId(context) + "_rowreorder");
}

So here we have 2 possibilities to prevent datasource rotating:

  1. Don't use List as dataTable value
  2. Create own org.primefaces.component.datatable.feature.DraggableRowsFeature returning false in shouldDecode() method.

Practice:

I modified bean file like this:

@Named("rowReorder")
@ViewScoped
public class RowReorder implements Serializable {

    private static final Logger log = LoggerFactory.getLogger(RowReorder.class);
    private Set<Data> dataList = new LinkedHashSet<>();


    public void addData() {
        Data data = new Data();
        data.setId(dataList.size() + 1);
        data.setName("Data " + data.getId());
        dataList.add(data);
        log.warn("{} {}", Integer.toHexString(data.hashCode()), data.getId());
    }

    public void removeData(Data data) {
        dataList.remove(data);
    }

    public void reorder(ReorderEvent event) {
        List<Data> list = new LinkedList<>(dataList);

        int fromIndex = event.getFromIndex();
        int toIndex = event.getToIndex();
        if(toIndex >= fromIndex) {
            Collections.rotate(list.subList(fromIndex, toIndex + 1), -1);
        }
        else {
            Collections.rotate(list.subList(toIndex, fromIndex + 1), 1);
        }
        dataList.clear();
        dataList.addAll(list);
    }

    /**
     * Getters, Setters
     */

    public Set<Data> getDataList() {
        return dataList;
    }
}

And now it firstly submitting values to model and rotating list on INVOKE_APPLICATION phase.

like image 117
antonu17 Avatar answered Feb 15 '23 14:02

antonu17