当前位置: 首页 > 面试题库 >

JSF中的递归(c:forEach与ui:repeat)

农飞翔
2023-03-14
问题内容

我正在尝试通过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中有一些关于构建递归的参考(例如,Facelets中的c:forEach与ui:repeat)。常见问题似乎是将构建时组件和渲染时组件/标签混合在一起。就我而言:

  • 我的复合组件实际上是一个标记,在构建树时执行
  • ui:repeat是实际的JSF组件,在呈现树时会对其进行评估

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

Facelets中c:forEach与ui:repeat
文章对此有一个单独的部分(递归)。建议c:forEach改用。我试过了,但是它仍然给我相同的东西StackOverflowError,但是我却无法理解。

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


问题答案:

JSF的内置声明性标记不适用于处理这种递归。JSF构建了一个有状态组件树,该树在请求之间持久存在。如果在后续请求中还原了视图,则视图状态可能无法反映模型中的更改。

我赞成命令式方法。我有两种选择:

  • 使用该binding属性将控件(例如某种形式的面板)绑定到提供UIComponent实例及其子级的后备bean- 您编写代码实例化,UIComponent并添加所需的任何子级。请参阅binding属性合同的规范。
  • 编写自定义控制,实现一些:一UIComponent; 一个Renderer; 标签处理程序;元数据文件(适当删除-根据您的工作以及JSF的版本和版本来执行其中的一些或全部操作)。

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

更新:
如果使用的是非常有用的OmniFaces库(如果还没有,则应该使用),<o:tree>该库没有html生成,而是专门为支持此类用例而设计的。

<o:tree value="#{bean.treeModel}" var="item" varNode="node">
    <o:treeNode>
        <ul>
            <o:treeNodeItem>
                <li>
                    #{node.index} #{item.someProperty}
                    <o:treeInsertChildren />
                </li>
            </o:treeNodeItem>
        </ul>
    </o:treeNode>
</o:tree>

编辑:

这是一种模型驱动的方法,不涉及编写自定义组件或支持bean生成的组件树。这有点丑陋。

Facelets视图:

<?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>

托管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;
  }
}

树节点:

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;
  }
}

输出:

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

我仍然会硬着头皮写一个自定义的树控件。



 类似资料:
  • 我对函数式编程很陌生,尤其是下面使用的Scheme。我正在尝试使以下函数是递归的,尾递归的。基本上,该函数的作用是对两个字符串的对齐方式进行评分。当给定两个字符串作为输入时,它会比较每个“列”字符,并根据在称为 scorer 的函数中实现的评分方案(由下面的代码中的函数调用)来累积该对齐的分数。 我有一个想法,用一个帮助函数来累积分数,但我不太确定如何去做,因此我该如何让下面的函数尾递归呢?

  • 如果说在任何地方都使用递归,那么可以使用for循环,对吗?如果递归通常比较慢,那么将其用于循环迭代的技术原因是什么? 如果总是可以将递归转换为for循环,那么有经验法则吗?

  • 本文向大家介绍数据结构 二叉树的递归与非递归,包括了数据结构 二叉树的递归与非递归的使用技巧和注意事项,需要的朋友参考一下 数据结构 二叉树的递归与非递归 实例代码:  先序遍历(递归法)   后序遍历      感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

  • 本文向大家介绍C#中的尾递归与Continuation详解,包括了C#中的尾递归与Continuation详解的使用技巧和注意事项,需要的朋友参考一下 这几天恰好和朋友谈起了递归,忽然发现不少朋友对于“尾递归”的概念比较模糊,网上搜索一番也没有发现讲解地完整详细的资料,于是写了这么一篇文章,权当一次互联网资料的补充。:P 递归与尾递归 关于递归操作,相信大家都已经不陌生。简单地说,一个函数直接或间

  • 在阅读了关于ForkJoinPool的文章之后,我尝试了一个实验,测试与普通递归相比,实际上有多快。 我以递归的方式计算了文件夹中的文件数,令我满意的是,简单的递归比执行得更好 这是我的密码。 递归任务 纯递归 null

  • 前面几节介绍了两个可以方便地用递归与迭代实现的函数。本节要比较递归与迭代方法,介绍为什么程序员在不同情况下选择不同方法。 递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。递归与迭代都涉及重复:迭代显式使用重复结构,而递归通过重复函数调用实现重复。递归与迭代都涉及终止测试:迭代在循环条件失败时终止,递归在遇到基本情况时终止。使用计数器控制重复的迭代和递归都逐渐到达终止点:迭代一直修改计