如何部分刷新ui:repeat?

18

我们有一个基于 JBoss 7.1、使用 JSF2 和 Primefaces 3.3 构建的 Web 应用程序。

在其中一个页面上,有一个 ui:repeat 显示了 10 个项目;然后用户可以点击某种“显示更多”按钮,通过 Ajax 显示 10 个更多的项目。用户可以一直点击“显示更多”按钮,直到没有更多项目为止。注意:这不是分页,每次单击“显示更多”时,显示的列表都会变长。

实际上,当用户单击该按钮时,服务器会返回旧项目和新项目,并且 JSF 的客户端必须每次都通过 jQuery 重建整个重复器。

我们希望找到更好、更高效的解决方案。旧项目在第 n-1 次调用和第 n 次调用之间不会改变,因此如果服务器能够仅通过 ajax 返回 10 个新项目,则效率将更高。

在 JSF2 中是否可能实现?JSF 似乎并不真正符合这种递归渲染的要求。唯一可能帮助我们的组件可能是 TreeNode 组件,但它似乎有点像 hack :-/


如果我理解正确的话,您有一个包含n个项目的初始列表,首先显示10个,然后想要显示接下来的10个,以此类推,就像分页一样。我猜您在后端bean中有两个列表:一个是完整的列表项,另一个是每次ajax请求时填充10个长度的第二个列表,对吗? - Luiggi Mendoza
这很简单,但不是分页:我们想要显示前10个项目,然后是前20个项目(10个旧的,10个新的),就像Facebook上的墙一样。 - Xavier Portebois
@XavierPortebois:我也想知道你是使用分页还是直接添加到列表中。我编辑了你的问题以澄清(请随意更正)。 - sleske
2个回答

10

标准JSF API中没有这种功能。在PrimeFaces中也没有类似的东西。请参见末尾的更新。

但是OmniFaces <o:componentIdParam>可能正是您要寻找的。它允许您根据特定的请求参数(可以是组件ID或客户端ID)让JSF仅呈现组件树的子集。您可以基本上只使用jQuery的$.get()重新加载<ui:repeat>以及作为请求参数的起始索引,并使用jQuery的$.append()将其附加到HTML DOM中。

这里是一个完整的启动示例。视图:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:o="http://omnifaces.org/ui"
>
    <f:metadata>
        <o:componentIdParam componentIdName="componentId" />
    </f:metadata>
    <h:head>
        <title>Stack Overflow Question 11364006</title>
        <script src="http://code.jquery.com/jquery-latest.min.js"></script> <!-- Substitute with PrimeFaces' one, if necessary. -->
    </h:head>
    <h:body>
        <ul id="items">
            <ui:repeat id="itemsRepeater" value="#{bean.items}" var="item">
                <li>#{item}</li>
            </ui:repeat>
        </ul>
        <input type="button" id="showMore" value="Show more"/>
        <h:outputScript>
            $("#showMore").click(function() {
                $items = $("#items");
                var params = { start: $items.find("li").length, componentId: "itemsRepeater" };
                $.get(location, params, function(html) {
                    $items.append(html);
                });
            });
        </h:outputScript>   
    </h:body>
</html>

支持bean:
@ManagedBean
@RequestScoped
public class Bean {

    private List<String> items;

    @ManagedProperty("#{param.start}")
    private int start;

    @PostConstruct
    public void init() {
        // Just a stub. Do your thing to fill the items.
        items = new ArrayList<String>();
        int size = start + 10;

        for (int i = start; i < size; i++) {
            items.add("item " + (i + 1));
        }
    }

    public void setStart(int start) {
        this.start = start;
    }

    public List<String> getItems() {
        return items;
    }

}

更新: 在当前展示应用程序的<o:componentIdParam>页面的“可展开列表”示例中可以找到实时演示。

更新2: PrimeFaces p:datascroller具有按需滚动的懒加载功能。


我今晚开始度假,所以无法尝试,但听起来很有前途。Omnifaces是否容易与Primefaces兼容? - Xavier Portebois
1
是的。而且,OmniFaces展示Web应用程序使用PrimeFaces进行UI设计。至于启动示例,它可以复制粘贴并运行(当然,在自动组织导入之后),对我来说很好用。 - BalusC

1
对于这种情况,我完全绕过JSF并使用JAX-RS服务与jQuery AJAX。这种方法将帮助您构建比仅使用JSF的AJAX支持更好的应用程序。以下是基本技术。
在XHTML页面中,只需为列表设置占位符:
<div id="item_list"></div>

<a href="#" onclick="loadNext();">Load more...</a>

当页面加载时,发起一个AJAX请求来填充初始列表。以下是示例代码。可能会有拼写错误,但您可以理解意思。
var nextStart = 0;

$(function() {
    loadNext();
});

function loadNext() {
    $.ajax({
        type: "GET",
        url: "api/items?start=" + nextStart,
        success: function(data) {
            appendToList(data);
            nextStart += data.length;
        }
    });
}

function appendToList(data) {
    //Iterate through data and add to DOM
    for (var i = 0; i < data.length; ++i) {
        $("#item_list").append($("<p>", {text: data.productName}));
    }
}

是的,在许多情况下这很有效。然而,您不能在表格内使用JSF组件(例如按钮)。 - sleske

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