JSF 页面中组件(面板)的可变顺序

Variable order of components (panels) in JSF page

我想知道 JSF(或其任何框架,例如 Primefaces)是否有可能在页面中显示组件的可变顺序,或者更准确地说是面板。

例如,向用户显示 3 个不同的内容部分(这些是从 1 到 3 的面板)。但是,在设置中,他应该设置这些内容部分的显示顺序(比如,panel3 应该在顶部,panel1 在中间,panel2 在底部)。

我想到的唯一想法是使用组件绑定或某种不可见的排序数据列表,但我宁愿避免这些方法,因为它涉及太多变通方法。

这在 JSF 中实际上相当容易。该框架使您能够将自己插入生命周期的不同渲染和构建事件中。因此,例如,您可以轻松地使用此功能在呈现视图并发送到浏览器之前随机播放组件。

代码

这是一个定义了五个 p:panel 组件的 XHTML 页面示例。每次重新加载页面时,组件将被打乱顺序并以不同的顺序显示。您可以轻松地调整它以按照您喜欢的顺序或根据某些配置设置显示它们;

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Shuffle test</title>
    </h:head>
    <h:body>
        <h:panelGroup>
            <p:panel header="First"/>
            <p:panel header="Second"/>
            <p:panel header="Third"/>
            <p:panel header="Fourth"/>
            <p:panel header="Fifth"/>
            <f:event listener="#{shuffleBackingBean.onShuffle}" type="preRenderComponent" />
        </h:panelGroup>
    </h:body>
</html>

f:event 标签的位置可以看出,我们将自己插入父 h:panelGrouppreRenderComponent 阶段。这允许我们在该组件的渲染阶段之前接收到一个事件。

@Named
@ViewScoped
public class ShuffleBackingBean implements Serializable {
    public void onShuffle(ComponentSystemEvent event) {
        final List<UIComponent> components = new ArrayList<>(event.getComponent().getChildren());
        Collections.shuffle(components);

        event.getComponent().getChildren().clear();
        event.getComponent().getChildren().addAll(components);  
    }
}

上面的支持 bean 定义了 onShuffle 方法,并在调用时随机播放组件。如果您重新加载页面,组件将重新排列。

旁注

在组件列表的副本上执行 shuffle() 的原因是 JSF 使用基于 ArrayList 的自定义 ChildrenList class。实施是拙劣的,导致 Collections.shuffle() 崩溃并出现 IndexOutOfBoundsException。这正好解决了这个问题。

另一种解决方案

处理此问题的另一种方法是依赖某些提供内置排序的组件或使用在 h:panelGroup 组件上声明的绑定。这将允许您根据某些设置以编程方式填充此组件。但是,这会将大部分视图定义移出 XHTML 文件并移入 java class。如果您的面板中有很多子组件,它也会使事情稍微复杂化,如果它们彼此非常不同,则会使事情变得更加复杂。以编程方式定义所有内容可能会很麻烦。

结论

就个人而言,我更喜欢插入事件循环并仅移动或修改已在 XHTML 页面中定义的组件(按照上面的代码),因为这会将更多视图定义移动到它所属的 XHTML 文件中。