Dynamic ui:include inside ui:repeat. Is there a simple solution?
Asked Answered
T

3

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?

Terra answered 29/7, 2010 at 12:36 Comment(5)
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?Rodroda
@Rodroda 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.Terra
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.Rodroda
@Rodroda You can try with these classes. Need to register these components so it is not trivial to get this all working: pastebin.com/FSfYWehf DynamicInclude pastebin.com/Lpf4UxxQ UIRepeat (edited JSF 2.0.6 code I think) pastebin.com/bKk701xU DynamicIncludeHandlerTerra
this looks like exactly what I need. You sir, are a gentleman and a scholar! thanks!Rodroda
I
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

Ishmael answered 29/7, 2010 at 12:42 Comment(2)
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.Terra
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.Terra
M
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>
Michaella answered 24/6, 2015 at 15:31 Comment(0)
I
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

Ishmael answered 29/7, 2010 at 12:42 Comment(2)
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.Terra
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.Terra
S
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

Soubise answered 29/7, 2010 at 18:39 Comment(3)
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.Terra
Hmmm.. not sure I understand... so you have like Library { location, name, List<Book> 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...Soubise
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.Terra

© 2022 - 2024 — McMap. All rights reserved.