Search code examples
jsfdynamicjsf-2uirepeatuiinclude

Dynamic <ui:include src> depending on <ui:repeat var> doesn't include anything


I am new to JSF and I am struggling with dynamicaly rendering included pages. My code looks like this:

MenuBean

@ViewScoped
public class MenuBean implements Serializable {

    private MenuItem[] menuItems = new MenuItem[] {
        new MenuItem("page_1", "/page_1.xhtml"),
        new MenuItem("page_2", "/page_2.xhtml"),
    };

    private String selectedItemLabel;

    //...
}

MenuItem

public class MenuItem implements Serializable {

    private String label;
    private String page;

    //...
}

index.xhtml

    <ui:repeat var="menuItem" value="#{menuBean.menuItems}">                                    
        <h:panelGroup rendered="#{menuBean.selectedItemLabel eq menuItem.label}" layout="block">
            <h:outputText value="#{menuBean.selectedItemLabel}" />                              
            <ui:include src="#{menuItem.page}" />                                           
        </h:panelGroup>                                                                     
    </ui:repeat>                                                                            

The result is 2 buttons rendered. Whenever I click any button the label inside conditionaly rendered panelGroup appears but included page doesn't. If I change 'menuItem1' var from first ui:repeat it works but it is really unpredictable. For example if I hardcode setSelectedItemLabel parameter to 'page_1' then when I click to button_1 page_1 is displayed but even if I click to button_2 page_2 (!?) is displayed...


Solution

  • You're facing a view build time vs view render time problem. This is essentially the same problem which is already answered in detail in JSTL in JSF2 Facelets... makes sense? In that answer, replace "JSTL" with "ui:include". To the point, the <ui:repeat> runs during view render time, while the <ui:include> has already run during view build time before.

    You'll understand that the only solution is to replace the <ui:repeat> by another tag which runs during view build time, such as JSTL <c:forEach>.

    Note that I don't guarantee that it will solve the concrete functional requirement which you've in mind. Using JSTL may have undesirable "side effects" (which are however explainable, understandable and workaroundable).

    See also: