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?
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"/>
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:
List
as dataTable valueorg.primefaces.component.datatable.feature.DraggableRowsFeature
returning false in shouldDecode()
method.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With