9

I want to dynamically pick a facelet to render some item in my data list. The first try would be:

<ui:repeat value="#{panels}" var="panel">
  <ui:include src="#{panel.facelet}">
</ui:repeat>

But it won't work since src of ui:include is evaluated too early. The facelet information is truly dynamic, so I cannot use c:forEach (not really recommended to mix with facelets either). I guess it all boils down to finding a component based ui:include alternative.

Is there such thing or I need to write my own?

mrembisz
  • 12,722
  • 7
  • 36
  • 32
  • We continue to struggle with this same issue for years. I wonder is mrembisz did you ever find a better solution with JSF 2? If not could you share you custom solution? – cyberoblivion Apr 02 '15 at 12:44
  • @cyberoblivion we have implemented our own include component and adapted UIRepeat to work with it - mainly to allow multiple nesting levels. It still works in our production systems. But we moved away from JSF anyway. – mrembisz Apr 02 '15 at 18:14
  • so the solution is not something that would be useful to anyone else or you are prohibited from sharing the code? I am very interested in the code. – cyberoblivion Apr 02 '15 at 21:00
  • 2
    @cyberoblivion You can try with these classes. Need to register these components so it is not trivial to get this all working: http://pastebin.com/FSfYWehf DynamicInclude http://pastebin.com/Lpf4UxxQ UIRepeat (edited JSF 2.0.6 code I think) http://pastebin.com/bKk701xU DynamicIncludeHandler – mrembisz Apr 03 '15 at 07:38
  • this looks like exactly what I need. You sir, are a gentleman and a scholar! thanks! – cyberoblivion Apr 03 '15 at 10:35

3 Answers3

8

I think I've found that relatively simple solution you've been looking for.

I too started with a ui:include inside a ui:repeat like yours, but I accepted that I had to use a c:forEach, and the c:forEach worked great for dynamically getting a different set of xhtml/components to include even with user interaction changing things in the View like I think you have. It looked like this:

<c:forEach var="thing" items="#{view.things}">
        <ui:include src="#{thing.renderComponent}">
            <ui:param name="thing" value="#{thing}"/>
        </ui:include>
</c:forEach>

However, my ui:param wasn't working - every component I included was passed the same "thing"/Object even though we had successfully used different things/Objects to dynamically include different components.

That's when I found this post which inspired me to wrap my ui:include in a f:subview. And now everything is working great with the following code:

<c:forEach var="thing" items="#{view.things}" varStatus="loop">
    <f:subview id="thing_#{loop.index}">
        <ui:include src="#{thing.renderComponent}">
            <ui:param name="thing" value="#{thing}"/>
        </ui:include>
    </f:subview>
</c:forEach>
Community
  • 1
  • 1
Mike Saris
  • 93
  • 1
  • 9
2

c:forEach will solve it, why can't you use it?

Interesting article regarding that issue: https://rogerkeays.com/jsf-c-foreach-vs-ui-repeat

Roger Keays
  • 3,117
  • 1
  • 31
  • 23
DuduAlul
  • 6,313
  • 7
  • 39
  • 63
  • Thanks but c:forEach is evaluated once, when the view is built. In my case what's under #{panels} can change while user interacts with the page. – mrembisz Jul 29 '10 at 13:14
  • Marking this as an answer: it won't work for me mostly due to performance impact but will be fine for most. Using custom dynamic include for now. – mrembisz Apr 22 '11 at 13:51
0

I remember trying to do something similar using a custom tag and FaceletHandler etc. In the end all the little rendering-time issues made it not worth the effort. Mind you I was (and still am :-(...) using facelets for jsf 1.1, so not sure if this is better in the later versions.

How dynamic / how many different facelets do you have to deal with? The reason I ask is that you could (to borrow a term from the compiler wizards) unroll your loop. Instead of

<ui:repeat value="#{panels}" var="panel">
  <ui:include src="#{panel.facelet}">
</ui:repeat>

You could do

<custom:panelOneFacelet rendered="#{hasPanel1}" />
<custom:panelTwoFacelet rendered="#{hasPanel2}" />
<!-- etc... -->

And in your facelet, you would have something like :

<c:if test="#rendered" >
    <!-- EVERYTHING IN THE FACELET HERE!!!-->
</c:if>

This sort of low-tech approach is fine for a small controlled set, but if you have a very large and varying set of facelets this may not work.

Might I ask why, b/c sometimes with an understanding of the high-level, SO gurus may suggest much simpler ideas for accomplishing the same goal

Java Drinker
  • 3,127
  • 1
  • 21
  • 19
  • Basically we render a business form which is provided to us as a tree of data fields. What you describe here was our first try but it turned out to be very resource consuming since in each node we had a complete set of possible rendering options. We ended up implementing our own include tag + component which works fine but is a real pain to maintain due to complexity and jsf internals. We did this nearly 3 years ago and now plan on moving to jsf2. I was hoping some out-of-the-box alternative appeared by this time. – mrembisz Jul 29 '10 at 19:14
  • Hmmm.. not sure I understand... so you have like Library { location, name, List books} where Book { name, isbn, Author} where Author {name, age, ..} Something like that? And you would like to get that kind of structure to generate a tree of forms fields?... I must have this wrong, eh?.. This seems much too complex for me, I would break all that down to different forms and beans etc... – Java Drinker Jul 29 '10 at 21:34
  • You've got it right, this is exactly what we have and we don't know the record structure until runtime. As I said we have a working but quite complex solution. I would like to investigate the possibility of dropping our custom jsf include logic. – mrembisz Jul 29 '10 at 21:51