重新排序 p:包含输入的 p:dataTable 行
Reordering p: p:dataTable rows containing inputs
一列中有p:dataTable
和p:inputText
:
<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>
支持 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;
}
}
数据class:
public class Data implements Serializable {
private Integer id;
private String name;
/**
* Getters, Setters
*/
}
重新排序前的示例数据表:
--------------
|id | name |
--------------
| 1 | Data 1 |
| 2 | Data 2 |
| 3 | Data 3 |
| 4 | Data 4 |
--------------
重新排序后(将第 1 行移动到第 3 行):
--------------
|id | name |
--------------
| 2 | Data 1 |
| 3 | Data 2 |
| 1 | Data 3 |
| 4 | Data 4 |
--------------
我知道这是因为在 UPDATE_MODEL
阶段设置来自 p:inputText
的数据。我试图通过在 p:ajax
组件中指定 process="@none"
来阻止处理输入字段,但它不起作用。知道如何让 draggableRows
和 p:inputText
成为朋友吗?
第一个解决方案
我找到了解决办法!
它不处理具有属性 process="@none" partialSubmit="true"
的输入(实际上根本不提交)
如此完整 p:ajax 组件看起来像
<p:ajax event="rowReorder" listener="#{rowReorder.reorder}" update="dataTable" process="@none" partialSubmit="true"/>
第二种方案(如果需要提交数据)
理论:
让我们看看拖动行时发生了什么?
我们有 ajax 个请求强制 process="form:dataTable"
。在 APPLY_REQUEST_VALUES 阶段 DataTableRenderer
尝试调用 DraggableRowsFeature
的解码,这反过来会旋转列表元素(指定为数据表的值属性的列表)。所以在 UPDATE_MODEL_VALUES 阶段我们有一个旋转列表和输入组件,它想将它们的值提交给 Data
对象的 name
字段。但是请求参数在输入 id 中仍然包含旧行索引:它们是 form:dataTable:1:name = Data 2
、form:dataTable:2:name = Data 3
、form:dataTable:0:name = Data 1
(我添加了 3 行,并将第一行移到最后一行)。所以在这里我们得到了我们得到的。这样,如果我们需要在正确的地方提交数据,我们必须在 UPDATE_MODEL_VALUES 完成之前阻止我们的列表旋转,
稍后在 INVOKE_APPLICATION 阶段执行此旋转,并在 ajax 请求上呈现 dataTable:
在DraggableRowsFeature.decode()
中我们可以看到Collections.rotate()
仅在值是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.");
}
还有DraggableRowsFeature.shouldDecode()
方法。
public boolean shouldDecode(FacesContext context, DataTable table) {
return context.getExternalContext().getRequestParameterMap().containsKey(table.getClientId(context) + "_rowreorder");
}
所以这里我们有两种可能性来防止数据源旋转:
- 不要使用
List
作为 dataTable 值
- 创建自己的
org.primefaces.component.datatable.feature.DraggableRowsFeature
在 shouldDecode()
方法中返回 false。
练习:
我这样修改了 bean 文件:
@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;
}
}
现在它首先在 INVOKE_APPLICATION 阶段向模型和旋转列表提交值。
秘密是你的数据表的属性rowStatePreserved
,添加它:
rowStatePreserved="true"
<p:dataTable id="dataTable" value="#{rowReorder.dataList}"
var="row" draggableRows="true" rowKey="#{row.id}"
rowStatePreserved="true">
并保留此代码:
<p:ajax event="rowReorder" listener="#{rowReorder.reorder}" update="dataTable" process="@this"/>
在我的例子中,我在数据表列中使用了一个组合框,在我添加这个属性之后,当我使用 draggableRows 函数时,值不会超过一行更改为另一行。
我等着帮你。
其他简单的解决方案是在 rowReorder
开始时禁用输入:
<p:ajax event="rowReorder"
onstart="$(':input', PrimeFaces.escapeClientId('#{component.clientId}')).prop('disabled',true)"
update="@this"/>
请注意,#{component.clientId}
将 return 数据的客户端 ID table。
为避免丢失数据,您可以对输入进行 Ajax 化:
<p:column headerText="#{msg.value}">
<p:inputText value="#{item.value}">
<p:ajax/>
</p:inputText>
</p:column>
一列中有p:dataTable
和p:inputText
:
<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>
支持 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;
}
}
数据class:
public class Data implements Serializable {
private Integer id;
private String name;
/**
* Getters, Setters
*/
}
重新排序前的示例数据表:
--------------
|id | name |
--------------
| 1 | Data 1 |
| 2 | Data 2 |
| 3 | Data 3 |
| 4 | Data 4 |
--------------
重新排序后(将第 1 行移动到第 3 行):
--------------
|id | name |
--------------
| 2 | Data 1 |
| 3 | Data 2 |
| 1 | Data 3 |
| 4 | Data 4 |
--------------
我知道这是因为在 UPDATE_MODEL
阶段设置来自 p:inputText
的数据。我试图通过在 p:ajax
组件中指定 process="@none"
来阻止处理输入字段,但它不起作用。知道如何让 draggableRows
和 p:inputText
成为朋友吗?
第一个解决方案
我找到了解决办法!
它不处理具有属性 process="@none" partialSubmit="true"
如此完整 p:ajax 组件看起来像
<p:ajax event="rowReorder" listener="#{rowReorder.reorder}" update="dataTable" process="@none" partialSubmit="true"/>
第二种方案(如果需要提交数据)
理论:
让我们看看拖动行时发生了什么?
我们有 ajax 个请求强制 process="form:dataTable"
。在 APPLY_REQUEST_VALUES 阶段 DataTableRenderer
尝试调用 DraggableRowsFeature
的解码,这反过来会旋转列表元素(指定为数据表的值属性的列表)。所以在 UPDATE_MODEL_VALUES 阶段我们有一个旋转列表和输入组件,它想将它们的值提交给 Data
对象的 name
字段。但是请求参数在输入 id 中仍然包含旧行索引:它们是 form:dataTable:1:name = Data 2
、form:dataTable:2:name = Data 3
、form:dataTable:0:name = Data 1
(我添加了 3 行,并将第一行移到最后一行)。所以在这里我们得到了我们得到的。这样,如果我们需要在正确的地方提交数据,我们必须在 UPDATE_MODEL_VALUES 完成之前阻止我们的列表旋转,
稍后在 INVOKE_APPLICATION 阶段执行此旋转,并在 ajax 请求上呈现 dataTable:
在DraggableRowsFeature.decode()
中我们可以看到Collections.rotate()
仅在值是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.");
}
还有DraggableRowsFeature.shouldDecode()
方法。
public boolean shouldDecode(FacesContext context, DataTable table) {
return context.getExternalContext().getRequestParameterMap().containsKey(table.getClientId(context) + "_rowreorder");
}
所以这里我们有两种可能性来防止数据源旋转:
- 不要使用
List
作为 dataTable 值 - 创建自己的
org.primefaces.component.datatable.feature.DraggableRowsFeature
在shouldDecode()
方法中返回 false。
练习:
我这样修改了 bean 文件:
@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;
}
}
现在它首先在 INVOKE_APPLICATION 阶段向模型和旋转列表提交值。
秘密是你的数据表的属性rowStatePreserved
,添加它:
rowStatePreserved="true"
<p:dataTable id="dataTable" value="#{rowReorder.dataList}"
var="row" draggableRows="true" rowKey="#{row.id}"
rowStatePreserved="true">
并保留此代码:
<p:ajax event="rowReorder" listener="#{rowReorder.reorder}" update="dataTable" process="@this"/>
在我的例子中,我在数据表列中使用了一个组合框,在我添加这个属性之后,当我使用 draggableRows 函数时,值不会超过一行更改为另一行。
我等着帮你。
其他简单的解决方案是在 rowReorder
开始时禁用输入:
<p:ajax event="rowReorder"
onstart="$(':input', PrimeFaces.escapeClientId('#{component.clientId}')).prop('disabled',true)"
update="@this"/>
请注意,#{component.clientId}
将 return 数据的客户端 ID table。
为避免丢失数据,您可以对输入进行 Ajax 化:
<p:column headerText="#{msg.value}">
<p:inputText value="#{item.value}">
<p:ajax/>
</p:inputText>
</p:column>