Recursion in JSF (c:forEach vs. ui:repeat)

前端 未结 2 1246
眼角桃花
眼角桃花 2020-12-06 12:05

I am trying to build a navigation tree via recursion in JSF. I have defined a navigationNode component as:


    

        
2条回答
  •  佛祖请我去吃肉
    2020-12-06 12:54

    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:

    • 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.

    UPDATE: If one is using the very useful OmniFaces library (you should if you don't already), there is the which has no html generation whatsoever but was specifically designed to support usecases like this.

    
        
            
    • #{node.index} #{item.someProperty}

    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:

    
    
    
      Facelet Tree
      
        

    The managed bean:

    @javax.faces.bean.ManagedBean(name = "tree")
    @javax.faces.bean.RequestScoped
    public class Tree {
      private Node 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 chapter2 = root.getKids().get(1);
        chapter2.getKids().add(new Node(chapter2, "Section A"));
        chapter2.getKids().add(new Node(chapter2, "Section B"));
      }
    
      public List> getTreeNodes() {
        return walk(new ArrayList>(), root);
      }
    
      private List> walk(List> list, Node node) {
        list.add(node);
        for(Node kid : node.getKids()) {
          walk(list, kid);
        }
        return list;
      }
    }
    

    A tree node:

    public class Node {
      private T value;
      private Node parent;
      private LinkedList> kids = new LinkedList<>();
    
      public Node(Node parent, T value) {
        this.parent = parent;
        this.value = value;
      }
    
      public List> 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 getLastChildLineage() {
        Node node = this;
        List 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.

提交回复
热议问题