jsfdynamicprimefacestabsuiinclude

JSF - Dynamic amount of tabs in tabView, with dynamic tab contents


I'm new to JSF and I'd like your suggestions on how to implement what I need the best way possible. Also first post in StackOverflow, so please excuse me if I'm not clear about something.

I've a p:tabView with a dynamic amount of tabs, generated with c:foreach in the following way, with a view scoped backing bean.

<p:tabView id="tabViewId">
    <c:forEach items="#{controller.list}" var="aTab">
          <p:tab title="#{aTab.label}">
              <ui:include src="#{aTab.firstUrl}"/>
          </p:tab>
    </c:forEach>
</p:tabView>

This works and I'm happy with it. Now the thing is each of my tabs have two possible views, and when a certain event is fired (a p:commandLink is clicked in one of the views) I'd like to change what is shown in the tab to the other view that tab has; in other words, I'd like to change the src view that is loaded inside the tab with ui:include. I've tried to return my desired view path in the method associated to this commandLink in the controller, but as expected it redirects to a new page instead of loading it inside the tab.

I'd like to keep the same controller instance when switching the tab contents (my controller is view scoped), and avoid binding.

I've came with two options.

First would be trying something similar to this (obtained from primefaces tabView different tab content with dynamic tabs), getting the view paths from my "aTab" instead of hardcoding them, and then trying to update the tab or the whole tabView on my commandLink after changing the boolean that switches between the two pages in the controller:

<ui:include
    src="#{curSearch.closeable ? '/sections/search/searchInstanceTab.xhtml' : '/sections/search/firstSearchTab.xhtml'}">
</ui:include>

The second one would be including both pages in every tab, but rendering only one using the boolean in the controller. This sounds much worse to me than the first option (it's suggested as an answer in the question I linked before).

Thanks in advance, please feel free to suggest any easier/better way.

::: EDIT :::

Finally did choose to have each tab in a separate xhtml file (being loaded with the c:foreach loop) and in each xhtml/tab all the possible views (also in separate xhtmls) ui:include'd with a rendered clause, hardcoded. Pretty smooth solution and works great, which possibly would allow to have dynamic contents inside each tab, with another c:foreach loop inside them and a list of pairs in the backing bean which would contain the xhtml names and booleans for rendering (that'd become a bit crazy).


Solution

  • The first option, the view build time conditional one, can lead to problems in some cases. E.g. you won't be able to rely on f:param in the conditional expression.

    This sounds much worse to me than the first option

    It's not much worse. Creating a bunch of components is not that pricey.

    It's not a black and white thing, but my general advice would be to not optimize unless you actually have a performance problem and profiling confirmed that this is the bottleneck (unlikely). Stick to the easiest path, which is using rendered.

    For Mojarra you can configure logging like this:

    <logger category="javax.enterprise.resource.webcontainer.jsf.timing">
        <level name="DEBUG"/>
    </logger>
    

    And then you'll see exactly how much time building the view (the Restore View JSF lifecycle phase) takes compared to your logic and rendering (the other phases):

    19:47:57,026 DEBUG [timing] (default task-3) [TIMING] - [7ms] : Execution time for phase (including any PhaseListeners) -> RESTORE_VIEW 1
    ...
    19:47:57,198 DEBUG [timing] (default task-3) [TIMING] - [172ms] : Execution time for phase (including any PhaseListeners) -> RENDER_RESPONSE 6