5

风景:

<h:form ...
  <p:dataTable value="#{myBean.list}" var="data" ...
     <p:column ...
        <h:commandButton action="#{controller.method(data.id)}" />
     </p:column>
  </p:dataTable>
</h:form>

控制器:

@ApplicationScoped
public class Controller {
   public String method(final Long dataId) {
        /* Do business */
        return URL_WITH_REDIRECT;
   }
}

生产者

(使用此处@ViewScoped描述的 CDI 注释)

@ApplicationScoped
public class Producer {
   @Named @ViewScoped @Producer
   public MyBean getMyBean() {
        final MyBean bean = new MyBean();
        bean.list = new ArrayList<Data>(); // where Data has a Long id field
        /* Do business and populate list */
        return bean;
   }
}

问题及其场景

  1. GET这页纸
    1. 豆子生产出来了
    2. 视图被渲染
    3. 响应发送到浏览器
  2. 点击按钮
    1. 数据被POSTed到服务器
    2. 阶段1-4 执行没有任何问题,并且@ViewScoped按预期使用 bean
    3. 阶段 5:controller.method调用data.id并访问1.1生成的 bean
    4. 方法返回重定向String
    5. !!制作人又来电了!!- 我们仍处于 APPLICATION_INVOCATION 阶段,但在实际方法调用之后
  3. 浏览器接收重定向
  4. GET下一页 ...

有效的半“驴”解决方案:

简而言之:点击时,将 id 复制到数据表之外,然后触发点击提交按钮。

h:commandButton表格列的内部添加:

onclick="$('input[id*=selectedDataId]').val('#{data.id}'); $('button[id*=callMethod]').trigger('click');"

表外:

<h:inputHidden id="{selectedDataId}"binding="#{selectedDataId}"/>
<p:commandButton type="submit"
                 id="callMethod"
                 label="Hidden button"
                 action="#{controller.method(selectedDataId.value)}"/>

最后它可以工作,但我无法弄清楚是什么原因导致第一种和基本方法重新初始化视图范围 bean。查看堆栈跟踪(见下文)似乎正在重建行。

问题:

有没有人对此问题有解释,也许还有一些注意事项?

堆栈跟踪

其中:getPipelinecheckSearchResults是用于检索支持表的列表的调用,这会导致调用生产者

堆栈跟踪

我已经浏览过的内容:

我已经阅读了以下文章/SO问题,但没有更好地理解上述(第一个)解决方案为何如此有效。

每次单击 dataTable 中的 commandButton 时,都会重新创建 ViewScoped bean

为什么 @PostConstruct 回调每次都会触发,即使 bean 是 @ViewScoped 的?JSF

如何将选定的行传递给 dataTable 中的 commandLink?

http://balusc.blogspot.de/2010/06/benefits-and-pitfalls-of-viewscoped.html

https://java.net/jira/browse/JAVASERVERFACES-1492

4

2 回答 2

1

我找到了一些用于 jsf/primefaces/ee-api/glassfish 等的资源来调试行为,所以这是答案:

简而言之

如果一个component

  • 触发导致重定向controller.method的操作 ( )
  • 并且被放置在一个datatable
  • datatable基于@ViewScopedbean生成其行

然后:

  • controller.method调用后,依赖的@ViewScopedbean将被重新生成(当然还有它的所有依赖项)datatable

测试:在2.1.7JSF 版本中。查看 的来源2.1.19,我希望那里有相同的行为。

细节

对于那些在寂寞的夏夜大声哭喊的人来说:“为什么?

导致此行为的“事件”链(参考来源):

  1. 用户单击表格行内的按钮。
  2. 数据被POSTed到服务器
  3. 第 1-4 阶段按计划进行
  4. APPLICATION_INVOCATION
    1. JSF 接收到 click 事件。重要提示:引用按钮的单击事件被包装在一个事件中,该事件包含有关表和单击发生的行号的信息。为简单起见:rowEvent & clickEvent
    2. 该事件在组件树中“广播”@UIViewRoot:794
    3. javax.faces.UIDataorg.primefaces.component.datatable.DataTable支持的祖父母p:datatable开始处理事件@UIData.broadcast(FacesEvent)
      1. broadcast方法首先保存最后选中行的索引
      2. 然后它选择由rowEvent
      3. 调度clickEvent孩子UIComponent,在我们的例子中Button
        1. 一切都很好,事件开始由ActionListener.processAction(ActionEvent)
          1. 这反过来调用controller.method返回重定向String,事情开始走下坡路
          2. 在方法结束时,redirectStringNavigationHandler
            1. 看到我们将要快速重定向的这个清除了在 line 中ViewMap删除所有@ViewScopedbean 的操作179。如果我们考虑一下,这是合乎逻辑的,因为我们正在退出。
      4. 在返回UIData.broadcast
        • 广播了内部事件,
        • 不知道某些内部事件导致了重定向,并且它所做的一切都会被扔进垃圾箱(因为302
        • 作为最后一个操作,尝试选择在步骤中保存其索引的行4.3.1
      5. 当然,要选择一行,它需要知道它的数据,这就是@ViewScoped重新生成表所需的 bean 的地方。

结束

注意

虽然我没有测试过,但我希望有相同的行为h:datatable, p:accordionPanel, p:carousel,等。简而言之,每个组件都是子类p:galleria化并且不提供- 感知方法。p:dataGridUIDataredirectbroadcast

于 2013-05-29T21:18:43.600 回答
0

除非我理解错误,否则您正在使用以某种方式限定在视图范围内的 bean(Seam 3、CODI 或您自己编写的自定义范围)。只要 JSF 生命周期在同一个视图上运行就可以了(这将是一个正确的假设),但是当您更改视图 id 时您会惊讶地发现您获得了视图范围 bean 的新实例?视图范围的全部目的是在 JSF 中保持相同的视图状态,只要您告诉 JSF 转到不同的视图 id,它就会创建一个新的视图状态。在我看来,您真正要寻找的是对话范围。

于 2013-05-28T16:23:39.130 回答