JSF 动态加载几乎可以工作 - 但没有命令按钮

发布于 2024-11-25 00:24:18 字数 2947 浏览 3 评论 0原文

我想在运行时动态包含 .xhtml 文件,您可能会这样称呼 JSF 前端到实体级别的继承层次结构。我期望 共有 100 x 30-50 行 xhtml 文件,仅加载 如果需要正确处理底层数据。一个典型的用例是 需要一两个 xhtml 文件来处理底层数据, 所以你可以想象我不想每次都加载完整的 100 个,它会 这样做就犯了愚蠢的罪行。

现在我知道关于这个话题有无数的讨论但是 经过无数个小时的研究,我一无所获。我正在使用 莫贾拉 2.1.1。

在下面的示例中,我最初 ui:include 一个“空”文件,一个带有 最少的内容(而不是 c:catch),然后单击命令按钮 更改要包含到 new.xhtml 的内容并更新容器 ui 的:包含 ajax。

new.xhtml 的内容显示在页面上,所以看起来不错。这 问题是我在 new.xhtml 中有一个 commandButton,当这个文件 动态包含命令按钮将不起作用 - 操作例程 从未被调用过。如果您使用 ui:include new.xhtml (而不是 #{bean.page}) 效果很好。所以有几个问题:

  1. 还有其他人能做到这一点吗?
  2. 如果没有,还有其他方法吗?
  3. 为什么如此明显且有用的事情却如此难以做到?

我今天玩了一下 jQuery load(),虽然我可以使用它加载文件 有各种各样的问题。我要加载的文件将包含 PrimeFaces 对话框,当由 jQuery 加载时,对话框似乎会瞬间闪烁 在屏幕上,然后永远消失,进入深渊。其他人举报 没有任何 CSS 样式的组件。

谢谢。

编辑:我应该明确表示我希望进行动态加载而不是 只是导航到不同的页面是因为我只想使用 primefaces 对话框,根据底层类型而有不同的对话框 数据。如果可能的话,这将是一种更简洁的方法。

索引.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:commandButton value="push" action="#{bean.doit}">
                <f:ajax render="load"/>
            </h:commandButton>
            <br/>
            <h:panelGroup id="load">
                <ui:include src="#{bean.page}"/>
                <ui:remove>
                <ui:include src="new.xhtml"/>
                </ui:remove>
            </h:panelGroup>
        </h:form>
    </h:body>
</html>

新.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core">
    <h:outputText value="new.xhtml"/>
    <br/>
    <h:commandButton value="inc" action="#{bean.inc}">
        <f:ajax/>
    </h:commandButton>
</html>

空.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"/>

Bean.java

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class Bean {

    private String page = "/empty.xhtml";

    public String doit() {
        page = "/new.xhtml";
        return null;
    }
    // getters & setters removed
}

I want to dynamically include .xhtml files at runtime, what you might call
the JSF front end to an inheritance hierarchy at the entity level. I expect
to have a total of 100 x 30-50 line xhtml files that would be loaded only
if required to correctly handle the underlying data. A typical use case is
that one or two xhtml files would be required to work on the underlying data,
so you can imagine that I don't want to load the full 100 every time, it would
be criminally stupid to do so.

Now I know that there are endless discussions out there on this topic but
after countless hours of research I have got precisely nowhere. I am using
Mojarra 2.1.1.

In the example below I initially ui:include an 'empty' file, a file with
minimal content (instead of c:catch), then click a commandButton which
changes the content to be included to new.xhtml and updates the container
of the ui:include with ajax.

The content of the new.xhtml is shown on the page, so this looks good. The
problem is that I've got a commandButton in new.xhtml, and when this file
is dynamically included the commandButton will not work - the action routine
is never called. If you use ui:include new.xhtml (instead of #{bean.page})
it works fine. So a few questions:

  1. Has anyone else got this working?
  2. If not is there another way of doing it?
  3. Why is something so incredibly obvious and useful so hard to do?

I had a play with jQuery load() today, and whilst I can load files using it
there are various issues. The files I want to load will contain PrimeFaces
dialogs and when loaded by jQuery the dialogs seemed to flash up momentarily
on the screen before disappearing, forever, into the abyss. Others report
components not having any css styling.

Thanks.

Edit: I should make it clear that my desire to do dynamic load rather than
simply navigating to a different page is because I just want to use
primefaces dialogs, a different one according to the type of underlying
data. It would be a neater way of doing it, if it's possible.

index.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:commandButton value="push" action="#{bean.doit}">
                <f:ajax render="load"/>
            </h:commandButton>
            <br/>
            <h:panelGroup id="load">
                <ui:include src="#{bean.page}"/>
                <ui:remove>
                <ui:include src="new.xhtml"/>
                </ui:remove>
            </h:panelGroup>
        </h:form>
    </h:body>
</html>

new.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core">
    <h:outputText value="new.xhtml"/>
    <br/>
    <h:commandButton value="inc" action="#{bean.inc}">
        <f:ajax/>
    </h:commandButton>
</html>

empty.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"/>

Bean.java

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class Bean {

    private String page = "/empty.xhtml";

    public String doit() {
        page = "/new.xhtml";
        return null;
    }
    // getters & setters removed
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

好倦 2024-12-02 00:24:18

问题是 ui:include 是由标签处理程序实现的
它在恢复视图期间执行,并且在调用期间不会重新执行
应用程序,因此当它修改组件树时您的应用程序逻辑
不知道这些变化。仅当使用部分状态时才如此
保存。

我将代码从 ui:include 标记处理程序复制到一个非常简单的自定义中
组件,并且至少可以触发操作方法,但不可靠。

解决方案是排除需要此动态 ui:include 功能的视图
使用 web.xml 中的条目进行部分状态保存,如下所示:

<context-param>
  <param-name>javax.faces.FULL_STATE_SAVING_VIEW_IDS</param-name>
  <param-value>/index.xhtml</param-value>
</context-param>

这已在多个包含的页面中使用多个命令按钮进行了测试
片段并且似乎工作可靠(使用 ViewScoped JSF beans 进行测试)。我已经
还动态包含一个包含 primefaces 对话框的文件,这可以工作
也可以。我希望这对其他人有用,因为它占据了很大一部分
虽然超出了我的日程安排,但至少现在可以正常工作,这很好。

干杯。

The issue turns out to be that ui:include is implemented by a tag handler
which is executed during restore view and not re-executed during invoke
application, so when it modifies the component tree your application logic
is not aware of the changes. This is true only when using partial state
saving.

I copied the code from the ui:include tag handler into a very simple custom
component and could at least get action methods to fire, but not reliably.

The solution is to exclude views requiring this dynamic ui:include functionality
from using partial state saving with an entry in web.xml like this:

<context-param>
  <param-name>javax.faces.FULL_STATE_SAVING_VIEW_IDS</param-name>
  <param-value>/index.xhtml</param-value>
</context-param>

This has been tested with multiple commandButtons in multiple included page
fragments and seems to work reliably (tested with ViewScoped JSF beans). I've
also dynamically included a file containing a primefaces dialog and this works
ok as well. I hope this is of some use to others as it's taken a good chunk of
time out of my schedule, but at least it's working now, which is good.

Cheers.

醉生梦死 2024-12-02 00:24:18

您可能会说 JSF 的想法是视图层内部有自己的 MVC 堆栈:视图层次结构的每个组件充当控制器(读取请求数据)、模型(存储其状态)和视图(呈现自身)。这就要求视图是稳定的:处理请求的视图必须与渲染表单的视图相同。视图树在生命周期中间发生的变化很难推理。

在您的情况下,问题可能是(我没有检查)bean 是请求范围的,因此当按钮显示时页面属性设置为“/new.xhtml”,但是当按钮的操作被发回时对于服务器来说,属性又是“/empty.xhtml”,因此重新创建的视图中不存在该按钮,因此它无法识别该操作,因此不会执行该操作。将 bean 的范围更改为 @ViewScoped 可能会有所帮助。

至于“为什么如此明显且有用的事情却如此难以做到?” - 在我看来,从这个问题来看,您做了一些主要且有问题的架构设计,没有遵循任何已建立的 JSF 实践,也没有学习您进行设计的库(您阅读了文档吗?还是只是示例?),您的设计只是基于您对图书馆应该如何工作的想象。我认为一些羞耻比批评和要求更合适。

You might say that the idea of JSF is that the view layer has its own MVC stack inside: each component of the view hierarchy acts as a controller (reads request data), model (stores its state) and view (renders itself). This requires that the view is stable: the view that processes requests must be the same as the view that rendered the form. Changes in the view tree that happen in the middle of the lifecycle are hard to reason about.

In your case, the problem is probably (I did not check) that the bean is request-scoped, so the page property is set to "/new.xhtml" WHILE THE BUTTON DISPLAYS, but when the action from the button gets sent back to the server, the property is "/empty.xhtml" again, so the button does not exist in the recreated view, so it does not recognize the action, so it does not execute it. Changing the scope of the bean to @ViewScoped might help.

As for "Why is something so incredibly obvious and useful so hard to do?" - it seems to me, judging from the question, that you made some major and questionable architectural design, WITHOUT following any established JSF practises and WITHOUT learning the library you made the design for (did you read the documentation? or just examples?), basing your design just on how you imagine the library should work. I would think that some shame would be more appropriate than critique and demands.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文