3

I have a set of jsf components that are statically generated from a set of excel files (they are updated by business people). Each generated file represents a business object that has slightly different data, and all of them belong to a same class.

In order to render this dynamically, the only solution I found was to set up a bunch of ui:fragment and dispatch to the right component at runtime:

<!-- IMPLEMENTATION -->          
<composite:implementation> 
    <ui:fragment rendered="#{cc.attrs.type eq 'cartcred'}">
        <limites:limites-cartcred  limite="#{cc.attrs.limite}"/>
    </ui:fragment>
    <ui:fragment rendered="#{cc.attrs.type eq 'cdcp'}">
        <limites:limites-cdcp limite="#{cc.attrs.limite}"/>
    </ui:fragment>
    <ui:fragment rendered="#{cc.attrs.type eq 'cheqpredatado'}">
        <limites:limites-cheqpredatado limite="#{cc.attrs.limite}"/>
    </ui:fragment>
    <ui:fragment rendered="#{cc.attrs.type eq 'confirming'}">
        <limites:limites-confirming limite="#{cc.attrs.limite}"/>
    </ui:fragment>
   <!-- many more lines -->
   <!-- many more lines -->
   <!-- many more lines -->
    <ui:fragment rendered="#{cc.attrs.type eq 'contacorr'}">
        <limites:limites-contacorr limite="#{cc.attrs.limite}"/>
    </ui:fragment>

But I found out that the perfomance of this is terrible. I tought that JSF would only render a single component, but it seems that it is rendering all of them and "hiding" the others at runtime.

Is there a more efficient way of achieving my goal? I want to render a single component based on runtime information about a business class (much like an if-then-else), but I can only determine what is the component to render at runtime.


Clarification: what happens is that each component referenced by limites:limites* is a huge complex page with lots of other components. At runtime, the parameter named type' will decide what component to render. But my tests show that if I only render one component, but leave the otherui:fragments` (even knowing that they will not be rendered), it will render much slower than if I remove the components.

So if my page is exactly like this:

<composite:interface>
    <composite:attribute name="type" required="true" />
    <composite:attribute name="limite" required="true" />
</composite:interface>         
<composite:implementation> 
    <ui:fragment rendered="#{cc.attrs.type eq 'cartcred'}">
        <limites:limites-cartcred  limite="#{cc.attrs.limite}"/>
    </ui:fragment>
</composite:implementation>

it will render much (around 10x) faster than the initial version, even though the parameters are the same. I suspect that JSF will create the entire component tree and only at runtime it will decide (depending on the supplied parameter) if it will render each other or not.


Edit

Almost there. I just need to include my composite component dynamically. I tried evaluating an ELExpression but that didn't work. What I need is a way of accessing the current scope within the component creation, and using that to generate the proper file name:

//obviously, ELExpressions don't work here
Resource resource = application.getResourceHandler().createResource("file-#{varStatus.loop}.xhtml", "components/dynamicfaces");
Miguel Ping
  • 18,082
  • 23
  • 88
  • 136

2 Answers2

9

Yes, the rendered attribute evaluates during render time, not during build time. Yes, it is relatively terrible. Imagine that one such a condition costs 1ms, evaluating ten of them would take in total 10 times longer, 10ms. If you in turn have ten of those components in a paginated table, the webapp loading time would take 0,1 second longer. About one eyeblink longer. But if you don't paginate and/or are using MSIE as reference browser, then it would take much longer. Are you paginating the data and testing in proper browsers?

Best what you could do is to replace the <ui:fragment> by JSTL tags like <c:if>/<c:choose> so that it evaluates during build time, not during render time. Or, alternatively, build the component tree in the backing bean constructor instead of in the view.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I cannot paginate, because the generated page is being used for printing purposes. Since the above page is also generated from xls metadata, what I really want to know is how can I ensure that the `fragment` contents are only "processed" if the rendered condition evaluates to `true`. Besides, my reference browser is MSIE6. I should probably get another job... – Miguel Ping May 16 '11 at 13:03
  • MSIE has an extremely poor HTML table rendering performance. I doubt if your problem is actually caused by EL. It's extremely fast. Try testing the same page in other browsers, e.g. Firefox or Chrome -just for your own reference. – BalusC May 16 '11 at 13:05
  • JSTL didn't work as I expected. My real case was a little more contrived than the example I posted, but thanks anyway. – Miguel Ping May 19 '11 at 12:41
3

One possibility might be to use the binding attribute to access a container component from inside your managed bean and build the component tree from the java side. That way you could include only the needed components, unneeded components won't be evaluated at all.

JSP:

<h:panelGroup binding="#{managedBean.panel}"/>

Managed Bean:

private UIPanel panel;

// getter and setter


// Action method, might also work in a @PostConstruct
public String showComponent() {
    if (showComponent1) {
        UIOutput component1 = new HtmlOutputText();
        component1.setValue("Hello world!");

        getPanel().getChildren().add(component1);
    }

    return "viewId";
}

I haven't used this together with composite components yet, this question seems to have some more details and an example application regarding using this with composite components.

Edit: Regarding your edit, you can also evaluate EL expressions in your managed bean like this:

FacesContext facesContext = FacesContext.getCurrentInstance();
ELContext elContext = facesContext.getELContext();
ExpressionFactory exprFactory = facesContext.getApplication().getExpressionFactory();
ValueExpression expr = exprFactory.createValueExpression(elContext, "#{expr}", String.class);
String value = (String) expr.getValue(elContext);
Community
  • 1
  • 1
Jörn Horstmann
  • 33,639
  • 11
  • 75
  • 118
  • This worked, although it didn't solve my particular problem I think it was a correct answer. – Miguel Ping May 18 '11 at 14:52
  • @Miguel, thanks, please see my update for an example of how to use el expressions from java code. – Jörn Horstmann May 18 '11 at 15:04
  • Hehe thanks, I already tried evaluating the ELExpression on the bean, but for some reason it didn't evaluate properly; it probably couldn't resolve the variables I was trying to access at the time the `setComponent` is called. Sometimes you just have to move on. – Miguel Ping May 19 '11 at 12:43