0

一个很正常的情况:

我们有一个“portlet”复合组件,它有两种状态:展开和折叠。Portlet 以展开的形式开始,但用户可以通过单击它们来折叠它们。这个状态应该保存到会话中,这样一旦用户刷新页面或导航到新页面,portlet 就会记住它们是否展开/折叠。

这是问题的核心:您将如何将这种状态保存实际实现到可以在页面上多次插入的组件中?

一些背景/想法:

使用会话范围的 bean 很容易为一个 portlet 实现状态保存。通过创建固定数量的会话范围 bean 或将不同的属性声明到一个 bean 中,也可以支持固定数量的 portlet。但是,我什至不想提及为什么这些方法不好。

我最初的想法是在单个会话 bean 下创建一个 Map(或一些对象结构)来保存所有 portlet 的状态。但是,我不能直接从 JSF 引用这些数据(这样 JSF 也会更新它们)。我考虑编写一个特定的 getter/setter 来获取/更新地图中的正确值,但这是不可能的,因为在执行 getter 和 setter 期间没有识别数据。

但是,很明显我需要将状态保存到会话 bean 中。通过使用提交的数据发布表单f:ajax并执行特殊方法,我可以很容易地进行状态保存。listener为了支持组件的多个实例,我可以使用(多个实例)请求范围的 bean 来处理每个展开/折叠。但是,为了实际发布标识 portlet 及其状态的数据,我需要首先在呈现时将其插入到表单字段中。

那么,如何在渲染时为每个 portlet 提供正确的数据(在这种情况下实际上只是状态布尔值/枚举,但考虑应该处理更多数据的情况)?

看起来:

  • h:inputHidden并且h:inputText不支持设置除 value-attribute(指向 )以外的初始值#{portletBean.portletState}
  • 我无法在创建时自动加载具有正确初始值的请求 bean,因为没有可用的识别信息。

再一次感觉我错过了一些东西......指针?

我想至少一个答案是使用UIComponent而不是复合组件,但我想将其保留为复合组件,因为它包含大量原始 HTML 元素。

更新:

  • 有一个相当相似的问题,建议使用视图范围的 bean。但是,我认为这在这里不可行。
4

3 回答 3

0

我使用以前解决的 hack 从 JS 调用 bean 方法

创建了一个空的、不可见的表单,其中h:commandButtonf:ajax内部调用listener.

创建了一个单独的按钮来启动展开/折叠事件。从该按钮启动 javascript 方法,该方法为展开/折叠设置动画,同时单击表单内的隐藏按钮。所有需要的数据都使用该listener属性发送。侦听器以会话 bean 为目标,它检查/更新由组件的客户端 ID 标识的状态映射。

这很简单,不知道为什么我之前没有意识到......

然而,它仍然远非完美,它没有回答如何使用默认值启动请求范围的 bean 以将它们用作存储/传输形式。

于 2010-09-28T17:31:20.437 回答
0

您可以使用 taglibs 执行此操作,您可以将参数传递给标签:

将以下内容添加到您的标记库中:

<tag>
    <tag-name>date</tag-name>
    <source>components/date.xhtml</source>
</tag>

然后这里是components/date.xhtml的内容

<ui:composition name="date_template">
    <h:panelGrid id="#{id}Panel" columns="1" border="0" cellpadding="0" cellspacing="0">
        <rich:calendar immediate="true" id="#{id}" popup="true" datePattern="dd.MM.yyyy"
                        enableManualInput="true" showApplyButton="true" cellWidth="12px" cellHeight="11px" style="width:100px"
                        value="#{dateForm.date}" required="#{required}" readonly="#{readonly}" validator="#{dateForm.validateDate}">
            <a4j:support event="onchanged" reRender="#{id}Panel,#{reRenderDate}"/>
            <ui:insert />
        </rich:calendar>

        <rich:message for="#{id}" errorClass="error" />
    </h:panelGrid>
</ui:composition>

然后你像这样使用它:

<adse:date id="dateTransfert" required="true"
           dateForm="#{dossierBean.dateTransfert}"
           readonly="false" reRenderDate="montantAncien,montantNouveau,montantEmolument">
     <a4j:support event="oninputblur" reRender="dateTransfertPanel,montantAncien,montantNouveau,montantEmolument"/>
</adse:date>

在这种情况下,传入了 dateForm,这是一个对象,在 taglib 中我们使用该对象的属性(dateForm.date)。另请注意 <ui:insert /> 可用于在 XHTML 中包含其他元素。

因此,您可以为每个 portlet 传递上下文。您甚至可以通过将 portletBean 作为定义 getPortletState() 方法的超类来制定此标准。

于 2010-09-28T13:18:25.613 回答
0

为此,我有一些可能的技巧。我会把它们贴在这里,但我不认为这些真的是要走的路:

渲染后直接使用 javascript 设置具有正确数据的表单字段:

window.addEvent('domready', function() {
    var portletState = '#{portletBean.getPortletState(cc.attrs.clientId)}';
    // find the field and set the data...
});

感觉很hacky。我不想走这条路。

使用组件的属性映射来保存数据:

<cc:interface>
    <cc:attribute name="portletState" default="#{portletBean.getPortletState(cc.attrs.clientId)}" />
</cc:interface>

从代码中访问它:

public void stateChangedCallback(String clientId) {
    UIComponent component = FacesContext.getCurrentInstance().getViewRoot().findComponent(clientId);
    String state = (String) component.getAttributes().get("portletState");
}

也感觉不太对。另外我不确定它是否有效(你可以在默认属性中实际使用 EL)。

通过在调用组件时提供数据,使用组件的属性映射来保存数据:

<portlet:portlet id="specificPortlet">
    <f:attribute name="portletState" value="#{portletBean.getPortletState(component.clientId)}" />
</portlet:portlet>

同样,非常笨拙并重复您的代码。

于 2010-09-28T12:53:21.843 回答