Reputation: 573
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?
Upvotes: 7
Views: 6384
Reputation: 2326
The issue has been finally fixed with PrimeFaces 14.0.12+ (https://github.com/primefaces/primefaces/issues/13310).
For the (optional) reorder ajax event this is enough:
<p:ajax
event="rowReorder"
listener="#{testView.onRowReorder}"
update="@this" />
No more workarounds needed for now...
Upvotes: 0
Reputation: 260
I've got the same problem using Primefaces 12.
Saddly the solutions above doesn't work for me, so i've to find another option:
Primefaces 12 offers the attribute draggableRowsFunction on the p:datatable element. If set, the default behavior described by antonu17 in his second solution is disabled. (see org.primefaces.component.datatable.feature.DraggableRowsFeature.decode(...))
This means the submit is processed normaly, including the correct model update in UPDATE_MODEL_VALUES-Phase. After that you can use the ajax event "rowReorder" with a listener method to get the ReorderEvent-Object with from/to index and shift the order in the list yourself.
That worked for me.
(Primefaces 7 worked fine with the first solution of antonu17)
Upvotes: 2
Reputation: 20253
Other simple solution is to disable the inputs on the start of rowReorder
:
<p:ajax event="rowReorder"
onstart="$(':input', PrimeFaces.escapeClientId('#{component.clientId}')).prop('disabled',true)"
update="@this"/>
Note that #{component.clientId}
will return the client ID of the data table.
To avoid loosing data, you can Ajaxify the inputs:
<p:column headerText="#{msg.value}">
<p:inputText value="#{item.value}">
<p:ajax/>
</p:inputText>
</p:column>
Upvotes: 0
Reputation: 699
The secret is the attribute rowStatePreserved
of your datatable, add it:
rowStatePreserved="true"
<p:dataTable id="dataTable" value="#{rowReorder.dataList}"
var="row" draggableRows="true" rowKey="#{row.id}"
rowStatePreserved="true">
And keep this code as like:
<p:ajax event="rowReorder" listener="#{rowReorder.reorder}" update="dataTable" process="@this"/>
In my case i was used a combobox into datatable column, and after i added this atrribute the value do not change more than one row to another when i used draggableRows function.
I wait help you.
Upvotes: 3
Reputation: 573
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.
Upvotes: 13