JSF中的递归(c:forEach vs. ui:repeat) [英] Recursion in JSF (c:forEach vs. ui:repeat)

查看:160
本文介绍了JSF中的递归(c:forEach vs. 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 vs ui:在Facelets中重复)。常见问题似乎是混合构建时和渲染时组件/标签。在我的情况下:




  • 我的复合组件实际上是一个标签,在树构建时执行

  • ui:repeat是一个实际的JSF组件,在呈现树时进行评​​估



子组件 navigation:navigationNode 实际上是在 ui:repeat 组件之前处理的?如果是这样,它用于#{child} 的对象是什么?它是否为null(似乎不是这样)?这里的问题是,实际创建子组件时甚至没有关心ui:repeat,所以每次创建一个新的子组件时,即使它不一定是想要的吗?



c:forEach vs ui:repeat在Facelets 文章中有一个单独的部分(递归)。建议是使用 c:forEach 。我试过这个,但它仍然给了我相同的 StackOverflowError ,带有我无法理解的不同痕迹。



<我知道我们也可以通过扩展 UIComponent 来构建组件,但是这种方法(在Java代码中编写html)似乎很难看。我宁愿使用MVC样式/模板。但是,如果没有其他方法,我是否必须将这种递归实现为UIComponent?

解决方案

JSF的声明性标签不适合处理这种递归。 JSF构建一个在请求之间保持的有状态组件树。如果视图在后续请求中恢复,则视图状态可能不会反映模型中的更改。



我赞成采用强制方法。我有两种选择:




  • 使用绑定属性进行绑定一个控件(例如某种形式的面板)到一个提供 UIComponent 实例及其子节点的支持bean - 你编写代码来实例化 UIComponent 并添加你想要的任何孩子。请参阅绑定属性合同的规范。

  • 编写自定义控件,实现以下部分: UIComponent ; a 渲染器;标签处理程序;元数据文件(根据需要删除 - 你可以根据你在做什么以及如何以及在哪个版本的JSF中执行部分或全部操作)。



也许另一种选择是选择已经执行此操作的第三方控件。






编辑:



这是一种模型驱动的方法,不涉及编写自定义组件或后端bean生成的组件树。这有点难看。



Facelets视图:

 < ?xml version ='1.0'coding ='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>
< h:head>< title> Facelet Tree< / title>< / h:head>
< h:body>
< ul>
< ui:repeat value =#{tree.treeNodes}var =node>
< h:outputText rendered =#{node.firstChild}
value =& lt; ul& gt; escape =false/>
< li>
< h:outputText value =#{node.value}/>
< / li>
< ui:repeat rendered =#{node.lastChild and empty node.kids}
value =#{node.lastChildLineage}var =ignore>
< h:outputText
value =& lt; / ul& gt; escape =false/>
< / ui:repeat>
< / ui:repeat>
< / ul>
< / h:body>
< / html>

托管bean:

  @javax.faces.bean.ManagedBean(name =tree)
@ javax.faces.bean.RequestScoped
public class Tree {
private Node< String> ; root = new Node(null,JSF Stuff);

@PostConstruct
public void initData(){
root.getKids()。add(new Node(root,Chapter One));
root.getKids()。add(new Node(root,Chapter Two));
root.getKids()。add(new Node(root,Chapter Three));
Node< String> chapter2 = root.getKids()。get(1);
chapter2.getKids()。add(new Node(chapter2,Section A));
chapter2.getKids()。add(new Node(chapter2,Section B));
}

public List< Node< String>> getTreeNodes(){
return walk(new ArrayList< Node< String>>(),root);
}

private List< Node< String>> walk(List< Node< String>> list,Node< String> node){
list.add(node);
for(Node< String> kid:node.getKids()){
walk(list,kid);
}
返回清单;
}
}

树节点:

 公共类节点< T> {
私人T值;
私有节点< T>父母;
private LinkedList< Node< T>> kids = new LinkedList<>();

公共节点(节点< T> parent,T值){
this.parent = parent;
this.value = value;
}

public List< Node< T>> getKids(){return kids;}
public T getValue(){return value; }

public boolean getHasParent(){return parent!= null; }

public boolean isFirstChild(){
return parent!= null&& parent.kids.peekFirst()== this;
}

public boolean isLastChild(){
return parent!= null&& parent.kids.peekLast()== this;
}

public List< Node> getLastChildLineage(){
Node node = this;
列表<节点> lineage = new ArrayList<>();
while(node.isLastChild()){
lineage.add(node);
node = node.parent;
}
返回血统;
}
}

输出:

  * JSF东西
o第一章
o第二章
+ A节
+ B节
o第三章

我仍然会咬紧牙关并编写自定义树控件。


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?

解决方案

JSF's 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:

  • Use the binding attribute to bind a control (e.g. some form of panel) to a backing bean that provides the UIComponent instance and its children - you write code to instantiate the UIComponent and add whatever children you want. See the spec for the binding attribute contract.
  • Write a custom control, implementing some of: a UIComponent; a Renderer; 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.


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:

<?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">
  <h:head><title>Facelet Tree</title></h:head>
  <h:body>
    <ul>
      <ui:repeat value="#{tree.treeNodes}" var="node">
        <h:outputText rendered="#{node.firstChild}"
                value="&lt;ul&gt;" escape="false" />
        <li>
          <h:outputText value="#{node.value}" />
        </li>
        <ui:repeat rendered="#{node.lastChild and empty node.kids}"
            value="#{node.lastChildLineage}" var="ignore">
          <h:outputText
              value="&lt;/ul&gt;" escape="false" />
        </ui:repeat>
      </ui:repeat>
    </ul>
  </h:body>
</html>

The managed bean:

@javax.faces.bean.ManagedBean(name = "tree")
@javax.faces.bean.RequestScoped
public class Tree {
  private Node<String> root = new Node(null, "JSF Stuff");

  @PostConstruct
  public void initData() {
    root.getKids().add(new Node(root, "Chapter One"));
    root.getKids().add(new Node(root, "Chapter Two"));
    root.getKids().add(new Node(root, "Chapter Three"));
    Node<String> chapter2 = root.getKids().get(1);
    chapter2.getKids().add(new Node(chapter2, "Section A"));
    chapter2.getKids().add(new Node(chapter2, "Section B"));
  }

  public List<Node<String>> getTreeNodes() {
    return walk(new ArrayList<Node<String>>(), root);
  }

  private List<Node<String>> walk(List<Node<String>> list, Node<String> node) {
    list.add(node);
    for(Node<String> kid : node.getKids()) {
      walk(list, kid);
    }
    return list;
  }
}

A tree node:

public class Node<T> {
  private T value;
  private Node<T> parent;
  private LinkedList<Node<T>> kids = new LinkedList<>();

  public Node(Node<T> parent, T value) {
    this.parent = parent;
    this.value = value;
  }

  public List<Node<T>> getKids() {return kids;}
  public T getValue() { return value; }

  public boolean getHasParent() { return parent != null; }

  public boolean isFirstChild() {
    return parent != null && parent.kids.peekFirst() == this;
  }

  public boolean isLastChild() {
    return parent != null && parent.kids.peekLast() == this;
  }

  public List<Node> getLastChildLineage() {
    Node node = this;
    List<Node> lineage = new ArrayList<>();
    while(node.isLastChild()) {
        lineage.add(node);
        node = node.parent;
    }
    return lineage;
  }
}

Output:

*  JSF Stuff
      o Chapter One
      o Chapter Two
            + Section A
            + Section B 
      o Chapter Three 

I would still bite the bullet and write a custom tree control.

这篇关于JSF中的递归(c:forEach vs. ui:repeat)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆