重新排序p:包含输入的p:dataTable行

5

有一个包含p:inputTextp:dataTable表格:

<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;
    }
}

数据类:

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 |
--------------

在重新排序之后(将第一行移动到第三行):

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

我理解这是由于在UPDATE_MODEL阶段从p:inputText设置数据导致的。我尝试通过在p:ajax组件中指定process="@none"来防止处理输入字段,但它不起作用。你知道如何让draggableRowsp:inputText结合起来吗?


调试显示,在 org.primefaces.component.datatable.feature.DraggableRowsFeaturedecode() 方法中,dataList 的旋转发生在 APPLY_REQUEST_VALUES 阶段。而将输入值设置到已经重新排序的 dataList 上则发生在 UPDATE_MODEL_VALUES 阶段,这就是问题所在。 - antonu17
只是猜测,但尝试在Data类中实现equals()和hashCode()方法。 - Jaqen H'ghar
我已经做了,只是没有显示出来,可能是默认设置的问题。 - antonu17
4个回答

11

第一种解决方案

我找到了一个解决方案! 使用属性 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 的 decode 方法,而这个方法又会旋转列表元素(该列表是指定为 dataTable 的 value 属性的列表)。因此,在 UPDATE_MODEL_VALUES 阶段,我们有了一个旋转后的列表和希望将其值提交到 Data 对象的 name 字段中的输入组件。但是请求参数仍然包含旧行索引作为输入 ID:它们是 form:dataTable:1:name = Data 2form:dataTable:2:name = Data 3form:dataTable:0:name = Data 1(我添加了 3 行,并将第一行移动到最后)。因此,我们得到了我们得到的结果。因此,如果我们需要在正确的位置提交数据,我们必须防止列表在 UPDATE_MODEL_VALUES 完成之前旋转, 并在 INVOKE_APPLICATION 阶段稍后执行此旋转,并在该 ajax 请求上呈现 dataTable:

DraggableRowsFeature.decode() 中,我们可以看到只有当值是 List 的实例时才会调用 Collections.rotate()

    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");
}

因此,我们有两种可能防止数据源旋转:

  1. 不要将 List 用作 dataTable 的值
  2. 创建自己的 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阶段向模型提交值并旋转列表。


如果您之后编辑了行,它仍然能正常工作吗?没有错误吗? - Kukeltje
它可以工作,但您需要将先前输入的数据提交给ViewRoot。例如 <p:inputText value="#{row.name}"> <p:ajax process="@this" update="@this"/> </p:inputText> - antonu17
1
选项2应该如何工作?除了通过hack DataTableRenderer之外,我没有看到任何插入自定义datatable功能的选项?感谢任何帮助。 - DmiN
2
@lastresort,这是不可能的,因为它没有面向组件的方式来进行扩展。我没有找到任何注册自定义DraggableRowsFeature的方法,除非对primefaces源代码进行修改... - DmiN
显示剩余7条评论

3

秘诀在于您的数据表的属性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"/>

在我的案例中,我在数据表格列中使用了一个组合框,在添加了这个属性之后,当我使用可拖动行功能时,值不会从一行更改到另一行。
我等待帮助。

0

另一个简单的解决方案是在rowReorder开始时禁用输入:

<p:ajax event="rowReorder"
        onstart="$(':input', PrimeFaces.escapeClientId('#{component.clientId}')).prop('disabled',true)"
        update="@this"/>

请注意,#{component.clientId}将返回数据表的客户端ID。
为了避免丢失数据,您可以将输入Ajax化:
<p:column headerText="#{msg.value}">
  <p:inputText value="#{item.value}">
    <p:ajax/>
  </p:inputText>
</p:column>

0
我在使用Primefaces 12时遇到了同样的问题。
可惜上面的解决方案对我不起作用,所以我不得不寻找另一种选择:
Primefaces 12在p:datatable元素上提供了draggableRowsFunction属性。 如果设置了该属性,那么antonu17在他的第二个解决方案中描述的默认行为将被禁用。(参见org.primefaces.component.datatable.feature.DraggableRowsFeature.decode(...))
这意味着提交将按照正常方式进行处理,包括在UPDATE_MODEL_VALUES阶段进行正确的模型更新。之后,您可以使用ajax事件"rowReorder"和一个监听方法来获取ReorderEvent-Object,其中包含了从/到索引以及在列表中移动顺序的信息。
这对我起作用了。
(Primefaces 7在antonu17的第一个解决方案中运行良好)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接