3

I'm trying to display a tree in JSF without hard-coding the depth of the tree I want to display. Maybe for one configuration I only want to show the leaves, maybe for another I want to show the leaves grouped by the nodes one level above them, etc. So for one configuration, it could be:

  • Question 1
  • Question 2
  • Question 3
  • Question 4

While for another configuration, the same underlying data structure could produce:

  • Category 1:

  • Question 1
  • Question 2

  • Category 2:
    • Question 3

  • Category 3:
    • Question 4

    With a hypothetical third configuration grouping the categories by super-categories, etc.

    I tried making a composite component that called itself recursively if the node had more children. I did this via <ui:fragment rendered="#{node.hasChildren}"> and -- surprise! -- this resulted in an infinite loop and a stack overflow.

    The project already has PrimeFaces installed, so I'm looking at PrimeFaces Tree and TreeNode. But that doesn't quite feel right; I don't want the tree nodes to be expandable in the user interface. I want everything to be fully expanded. I haven't gone too deeply down this rabbit hole so I suspect there may be a way to achieve this with Tree, but it feels like I'm going against the grain of what it's built to do.

    What's a good approach this problem?


    Edit: I'm trying to use <tree> and <treeNode>, but it's not working. My code looks like this:
    <p:tree value="#{cc.attrs.classification}" var="child">
        <p:treeNode>
            <ui:fragment rendered="#{child.childCount > 0}">
                [do node stuff]
            </ui:fragment>
            <ui:fragment rendered="#{child.childCount == 0}">
                [do leaf stuff]
            </ui:fragment>
        </p:treeNode>
    </p:tree>
    

    ... where classification implements the TreeNode interface. This results in errors to the console complaining that child is of type String and therefore cannot do any of the interesting things I'm asking of it. What am I doing wrong?

    BlairHippo
    • 9,502
    • 10
    • 54
    • 78

    2 Answers2

    4

    I tried making a composite component that called itself recursively if the node had more children. I did this via and -- surprise! -- this resulted in an infinite loop and a stack overflow.

    It failed because rendered attribute isn't evaluated during building the JSF component tree, but only during generating the HTML output. You basically need JSTL <c:if> instead of <some:component rendered>. It runs during building the JSF component tree, so you don't end up in infinitely including the composite itself.


    The project already has PrimeFaces installed, so I'm looking at PrimeFaces Tree and TreeNode. But that doesn't quite feel right; I don't want the tree nodes to be expandable in the user interface. I want everything to be fully expanded.

    The JSF utility library OmniFaces has exactly the component you're looking for, the <o:tree>. That component doesn't generate any HTML markup by itself, so you've all the freedom to declare the HTML/JSF markup the way you want, also for specific depth levels.

    Here's the showcase example. Note the links at the bottom of the page, they point to the source code which may be helpful/interesting as learning exercise. It isn't exactly trivial as you perhaps initially thought while developing the composite (which is after all not exactly the right tool for the job).

    As to distinguishing leaf nodes, the <o:tree> offers this possibility as follows:

    <o:tree value="#{bean.tree}" var="item" varNode="node">
        <o:treeNode>
            <o:treeNodeItem>
                <ui:fragment rendered="#{not node.leaf}">
                    ...
                </ui:fragment>
                <ui:fragment rendered="#{node.leaf}">
                    ...
                </ui:fragment>
                <o:treeInsertChildren />
            </o:treeNodeItem>
        </o:treeNode>
    </o:tree>
    
    Community
    • 1
    • 1
    BalusC
    • 1,082,665
    • 372
    • 3,610
    • 3,555
    • Just a note to tell that the showcase doesn't work, in reallity I don't think I ever seen it work... – Alexandre Lavoie Sep 23 '13 at 15:02
    • @Alex: you're not the first who reported that: https://code.google.com/p/omnifaces-showcase/issues/detail?id=3 Cause is yet undetermined. I can't really help as I never encountered it and I am not able to beam over myself to problematic machines to investigate it myself because the reporters are unable to naildown/describe the cause. I suspect something related to OS based SSL store. Try a different machine/OS/network. – BalusC Sep 23 '13 at 15:03
    • looks like it is related to Windows XP (SP3 in my case), working under Windows 2008 Standard Server, same browser, same network, if it can help you out! – Alexandre Lavoie Sep 23 '13 at 15:35
    • @BalusC: Thanks much for the options. Unfortunately, also gives me an infinite loop. Is there something obviously wrong with using it like this? `` (If that doesn't give you a clear enough idea of what I'm doing, I can edit the question to include a code snippet.) – BlairHippo Sep 23 '13 at 17:20
    • Ah, never mind, looks like the problem was I didn't have JSTL installed properly. – BlairHippo Sep 23 '13 at 17:49
    • turns out to be exactly what I was looking for. I was right; once I resolved my issues with PrimeFaces Tree (didn't properly override getData()), I found I was indeed swimming against the current of what that code was trying to do. Thank you very much for steering me in this direction. One question, though; is there an equivalent to the varStatus index value found in a node? I'd like to do some odd/even logic on the leaves getting shown. Am I just going to need to pull the final number off the string returned by varNode index? – BlairHippo Sep 24 '13 at 20:00
    • You're welcome. Fair request. This is unfortunately not supported in its current form, I didn't consider this use case. You can always post a feature request. For what are you going to need this? Setting some CSS classes? If so, did you consider CSS `:nth-child()` pseudoselector on `even` and `odd` values? – BalusC Sep 24 '13 at 20:06
    • Exactly, CSS classes. And no, I was unfamiliar with :nth-child(); it sounds very relevant to my interests. To the Googlewebs! – BlairHippo Sep 24 '13 at 20:09
    • Damn. :nth-child() has crappy IE support, which makes it a non-starter in my environment. Oh, well. String-processing that node index should do the trick. – BlairHippo Sep 24 '13 at 20:20
    • @BalusC: After I transitioned the code to make use of OmniFaces, a `` tag (inside ``) fundamentally stopped working -- the value of the `listener` attribute is getting ignored. Are there any known issues embedding PrimeFaces tags inside of OmniFaces tree, or is this probably something else? – BlairHippo Oct 01 '13 at 19:57
    • Works for me. Which PF version? Which JSF impl/version? Sure that you aren't nesting forms? – BalusC Oct 01 '13 at 20:23
    • @BalusC: It looks like my problem is being caused by a `` element nested inside the ``. I'm seeing indications that nesting iterative components is a known source of potential Fun (in the Dwarf Fortress definition of "Fun") where Ajax calls are concerned. I have a line on a workaround, at least: http://stackoverflow.com/questions/7316486/jsf-and-pajax-inside-pdatatable-inside-uirepeat – BlairHippo Oct 02 '13 at 19:49
    • Which Mojarra version? `` had indeed many state management related problems in earlier versions when being used in nested scenarios. – BalusC Oct 02 '13 at 19:51
    • Not sure how to tell which Mojarra version I'm using, but a coworker assures me it's "pretty old". I'm normally reluctant to embrace changes that involve updating our production server's environment, but this is pretty small potatoes compared to other stuff that's happening. Thanks for the insight, it's very much appreciated. – BlairHippo Oct 02 '13 at 20:16
    3

    Well. Since you can manage the tree structure from the ManagedBean you could do the recursion in Java, and just render the Tree.

    Last week I've done that. During the prerenderview I initialize the Tree using a method with recursion, and just something like:

     <p:tree value="#{bean.tree}" var="node" selection="#{bean.property}" >   
       <p:treeNode>  
         <h:outputText value="#{node}" /> 
       </p:treeNode>
     </p:tree>
    

    Hope it helps!

    Omar
    • 689
    • 3
    • 16
    • Have you read the question from top to bottom? Particularly the bottom? – BalusC Sep 19 '13 at 16:18
    • @BlairHippo states that is not feeling right because the tree is expandable but is meant to be expanded, and that, as you might know, is a property you can set when you're "building" the tree. So, why bothering with doing the markup when you get the things what you need?... And, I'm just giving a solution to a problem (I've solved before), is that so bad? – Omar Sep 19 '13 at 16:46
    • Then I apparently misunderstood the question. Nevermind then :) – BalusC Sep 19 '13 at 16:49
    • Nope. As far as I can tell, "node" implicitly gets turned into a String. If true, that makes the structure pretty much useless for me. – BlairHippo Sep 20 '13 at 21:23
    • Nope, actually you could extend the node class so you handle it as a bean with the properties, members and attributes you need. – Omar Sep 20 '13 at 23:53
    • @Omar: Please see my edit above. It's definitely treating the child element as a String, and I can't for the life of me figure out how to get it to treat it as a TreeNode. Any advice you can offer here is very welcome. – BlairHippo Sep 23 '13 at 13:25
    • I'll undelete my answer then. – BalusC Sep 23 '13 at 14:01
    • @BlairHippo Why do you want to do the recursion using markup when you can solve it from the managed bean? That's why I don't understand. And you can treat the node as an object from Java, I think it is simpler. – Omar Sep 23 '13 at 15:19
    • @Omar I'm starting to suspect there's something fundamentally wrong-headed about how I'm approaching this. I don't see a good way to display a tree-like structure of arbitrary depth without either doing something recursive in the JSF side or putting HTML in the backing bean. I suppose I could have the bean flatten the tree into a list that contains enough information for the JSF page to tell nodes (and their depth) apart from leaves, but that feels hack-y to me. Is there some obvious solution I'm overlooking? – BlairHippo Sep 23 '13 at 15:31
    • You put no HTML code in the backing bean, that would be so weird. What you do is something like this: https://gist.github.com/anonymous/f40b31fbac49812c0336 ... not hacky at all. – Omar Sep 23 '13 at 15:38
    • I appreciate the help, but it turns out I was right; once I got this code working (turns out my problem was failing to override getData() correctly in the backing structure), all the default formatting was at odds with what I was actually trying to do. Still, had BalusC not steered me to OmniFaces Tree, I probably would have wrestled the PrimeFaces Tree into submission. – BlairHippo Sep 24 '13 at 20:15
    • Is just kind of a perspective problem. Probably I just can't explain in a simpler (and clearer) way how to solve that issue. How I stated before, I created a custom node class that extends DefaultTreeNode, and with a recursive method (in the ManagedBean) you set every leaf in the tree. Then you just put the simple markup shown above. That was it ;) – Omar Sep 24 '13 at 20:48