Search code examples
jsf-2jstlcomposite-componentmojarra

<c:if> Tag does not reduce the component tree in composite components


We are developing an enterprise application with pretty large and complex views based on Mojarra 2.2.8. Our largest views produce JSF component trees of >20.000 nodes. Of coure we ran into performance issues, so we started to reduce the tree size by replacing <ui:repeat> with <c:forEach> and by replacing the rendered attribute with <c:if> blocks as recommended in this very informative post by BalusC. These measures reduced the component tree by a huge amount and brought significant improvements in performance.

One measure did not work out though: Our views contain severals tabs in a tab group. When we tried to remove contents of invisible tabs via <c:if> tags, the component tree of these tabs was still present and there was no benefit in performance.

We figured out, that the only difference between this usage of <c:if> and several other places was, that this time <c:if> was part of a composite component and its contents were injected through a <composite:insertChildren/> tag. The process of injecting the children of a composite component tag to its definition seems to somehow conflict with the <c:if> tag. We also tried to replace the insertChildren tag with a facet based approach, but the result remained the same.

To demonstrate this, here is a composite component, that wraps all its contents in a <c:if> tag, that always evaluates to false:

<composite:interface>
</composite:interface>

<composite:implementation>
    <c:if test="false"> 
        <composite:insertChildren />
    </c:if>
</composite:implementation>

This component shoud display nothing, even if it is used with a lot of child elements in a view. And it does display nothing but the component tree is still there and there is no performance benefit.

Unfortunately, this makes it impossible for us, to develop a performance optimized tab group composite component. Can anybody please explain, why this happens and if there is a way around it?

We are on mojarra 2.2.8, el-api 2.2.5, tomcat 8.0.


Solution

  • Technical problem is that the <c:if> in this construct only prevents inclusion (and execution) of the <cc:insertChildren> tag itself. The <cc:insertChildren> in turn is actually responsible for relocating any composite component children to the declared place in the composite component implementation so that they will be actually rendered. By default, a composite component does not render any children. The declared children are actually stored as a facet of the composite component.

    In other words, the <c:if> actually doesn't prevent those component children to end up in the component tree. It only prevents the <cc:insertChildren> from being invoked. The observed behavior is therefore working as designed. To achieve the desired behavior, you should actually be moving back the <c:if> to the client.

    <your:composite ...>
        <c:if test="#{false}">
            ...
        </c:if>
    </your:composite>
    

    I understand that this is unintuitive. Actually, you should take a step back from using composite components to compose the template. They are not well suited for that. You should use <ui:composition>, <ui:include>, <ui:decorate> or tag files for that instead. Composite components should only be used to compose a component.

    See also: