JSF 中的递归(c:forEach 与 ui:repeat)
我正在尝试通过 JSF 中的递归构建导航树。我已将 navigationNode
组件定义为:
<composite:interface>
<composite:attribute name="node" />
</composite:interface>
<composite:implementation>
<ul>
<ui:repeat value="#{navigationTreeBean.getChildrenForNode(cc.attrs.node)}" var="child">
<li><navigation:navigationNode node="#{child}" /></li>
</ui:repeat>
</ul>
</composite:implementation>
我的树声明为:
rootNode = new DefaultMutableTreeNode(new NodeData("Dashboard", "dashboard.xhtml"), true);
DefaultMutableTreeNode configurationsNode = new DefaultMutableTreeNode(new NodeData("Configurations", "configurations.xhtml"), true);
rootNode.add(configurationsNode);
我通过以下方式调用组件:
<nav:navigationNode node="#{rootNode}" />
问题是,这会导致 StackOverflowError
。
有一些关于在 JSF 中构建递归的参考(例如, c:forEach 与ui:在 Facelets 中重复)。常见的问题似乎是混合构建时和渲染时组件/标签。就我而言:
- 我的复合组件实际上是一个标签,在构建树时执行
- ui:repeat 是一个实际的 JSF 组件,在渲染树时对其求值
是子组件 navigation:navigationNode
实际上是在 ui:repeat
组件之前处理的吗?如果是这样,#{child}
使用什么对象?它是空的吗(看起来不是这样)?这里的问题是,子组件实际上是在不关心 ui:repeat 的情况下创建的,因此每次创建一个新的子组件时,即使它不一定是想要的?
Facelets 中的 c:forEach 与 ui:repeat< /em> 文章对此有一个单独的部分(递归)。建议改用c:forEach
。我尝试了这个,但是它仍然给我相同的 StackOverflowError ,并且带有我无法理解的不同跟踪。
我知道我们也可以通过扩展 UIComponent
来构建组件,但这种方法(用 Java 代码编写 html)看起来很难看。我宁愿使用 MVC 风格/模板。但是,如果没有其他方法,我是否必须将这种递归实现为 UIComponent ?
I am trying to build a navigation tree via recursion in JSF. I have defined a navigationNode
component as:
<composite:interface>
<composite:attribute name="node" />
</composite:interface>
<composite:implementation>
<ul>
<ui:repeat value="#{navigationTreeBean.getChildrenForNode(cc.attrs.node)}" var="child">
<li><navigation:navigationNode node="#{child}" /></li>
</ui:repeat>
</ul>
</composite:implementation>
My tree is declared as:
rootNode = new DefaultMutableTreeNode(new NodeData("Dashboard", "dashboard.xhtml"), true);
DefaultMutableTreeNode configurationsNode = new DefaultMutableTreeNode(new NodeData("Configurations", "configurations.xhtml"), true);
rootNode.add(configurationsNode);
I call component by:
<nav:navigationNode node="#{rootNode}" />
The problem is, this results in StackOverflowError
.
There are a few references to building recursion in JSF (for example, c:forEach vs ui:repeat in Facelets). The common problem seems to be mixing the build-time and render-time components/tags. In my case:
- My composite component is actually a tag, which is executed when the tree is built
- ui:repeat is an actual JSF component, which is evaluated when the tree is rendered
Is the child component navigation:navigationNode
actually processed before the ui:repeat
component? If so, what object is it using for #{child}
? Is it null (doesn't seem so)? Is the problem here that the child component is actually created without even caring about the ui:repeat and so each time a new child component is created even though it is not necessarily wanted?
The c:forEach vs ui:repeat in Facelets article has a separate section for this (recursion). The suggestion is to to use c:forEach
instead. I tried this, however it is still giving me the same StackOverflowError
, with different trace that I cannot make sense of.
I know that we can also build components by extending UIComponent
, but that approach (writing html in Java code) seems ugly. I would rather use MVC style / templates. However, if there are no other ways, do I have to implement this sort of recursion as UIComponent instead?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
JSF 的内置声明性标签不适合处理这种递归。 JSF 构建一个在请求之间保留的有状态组件树。如果在后续请求中恢复视图,则视图状态可能不会反映模型中的更改。
我赞成采取命令式的方法。在我看来,您有两个选择:
binding
属性将控件(例如某种形式的面板)绑定到提供UIComponent
实例及其子项的支持 bean - 您编写代码来实例化UIComponent
并添加您想要的任何子项。请参阅绑定
属性协定的规范。UIComponent
;渲染器;标签处理程序;元数据文件(适当删除 - 您执行部分或全部这些操作取决于您正在做什么以及如何以及在哪个 JSF 版本中)。也许另一种选择是选择已经执行此操作的第 3 方控件。
更新:
如果您正在使用非常有用的 OmniFaces 库(如果您还没有使用的话,您应该使用),这里有
没有任何 html 生成,但是专门设计用于支持此类用例。编辑:
这是一种模型驱动的方法,不涉及编写自定义组件或支持 bean 生成的组件树。有点丑。
Facelets 视图:
托管 bean:
树节点:
输出:
我仍然会硬着头皮编写一个自定义树控件。
JSF's built-in declarative tags are ill-suited for handling this sort of recursion. JSF builds a stateful component tree that is persisted between requests. If the view is restored in a subsequent request, the view state may not reflect changes in the model.
I would favour an imperative approach. You have two options as I see it:
binding
attribute to bind a control (e.g. some form of panel) to a backing bean that provides theUIComponent
instance and its children - you write code to instantiate theUIComponent
and add whatever children you want. See the spec for thebinding
attribute contract.UIComponent
; aRenderer
; a tag handler; meta-data files (delete as appropriate - you do some or all of these depending on what you are doing and how and in which version of JSF).Perhaps another option is to pick up a 3rd party control that already does this.
UPDATE:
If one is using the very useful OmniFaces library (you should if you don't already), there is the
<o:tree>
which has no html generation whatsoever but was specifically designed to support usecases like this.EDIT:
Here's a model-driven approach that doesn't involve writing custom components or backing-bean-generated component trees. It's kind of ugly.
The Facelets view:
The managed bean:
A tree node:
Output:
I would still bite the bullet and write a custom tree control.
在将我们的应用程序从 jsf 1.x 迁移到 2.x 时,我遇到了类似的问题(StackOverflowException)。如果您使用 c:forEach 方法进行 jsf 递归,请确保您使用 jstl 核心的新命名空间。
使用
而不是
这是我们正在使用的模式,适合您的场景。
client.xhtml
递归.xhtml
I had a similar issue(StackOverflowException) while migrating our app from jsf 1.x to 2.x. If you're using the c:forEach approach to jsf recursion, make sure you're using the new namespace for jstl core.
Use
instead of
Here's the pattern we we're using, adapted to your scenario.
client.xhtml
recursive.xhtml