2

Before I begin, my apologies if the wording of this question's title is confusing. I hope my explanation here will make it much clearer.

In a JSF template of my application, I want to include another template and pass a parameter to it that holds the results of an application method. In this parent template, I have:

<ui:repeat var="loopObject" value="#{ApplicationBean.objectList}">
    <ui:include src="anotherTemplate.xhtml">
        <ui:param name="firstParam" 
            value="#{ApplicationBean.initForOtherTemplate(loopObject)}" />
    </ui:include>
</ui:repeat>

It turns out, though, that initForOtherTemplate is not executed at this point and firstParam contains a reference to that method, rather than its return value, as I expected.

Actually, while initForOtherTemplate does have a return value, anotherTemplate.xhtml doesn't need it. However,the method does set up some other objects in ApplicationBean that this new template will use. For example, it sets values for importantInfo and importantInfoToo, which the other template needs.

anotherTemplate.xhtml contains:

<ui:remove>
    <!-- 
    When a parameter contains a method call, the method isn't executed until
    the parameter is referenced.  So we reference the parameter here and ignore
    the results.  There must be a better way.
    -->
</ui:remove>
<h:outputText value="#{firstParam}" style="display: none;" />
<h:outputText value="#{ApplicationBean.importantInfo}" />
<h:outputText value="#{ApplicationBean.importantInfoToo}" />

If this template didn't reference firstParam, then importantInfo and importantInfoToo wouldn't be set or have unpredictable values. This is very disappointing, because I expected initForOtherTemplate to be executed in the parent template, rather than here, which feels messy.

How can I get the assignment of the parameter to actually execute the method immediately rather than store a reference to it?

Mr. Lance E Sloan
  • 3,297
  • 5
  • 35
  • 50

1 Answers1

3

The <ui:repeat> is an UIComponent which runs during view render time. The <ui:include> is a TagHandler (like JSTL) which runs during view build time. So at the moment <ui:include> runs, the <ui:repeat> isn't running and thus the #{loopObject} isn't available in the EL scope at all.

Replacing <ui:repeat> by <c:forEach> should solve this particular problem.

<c:forEach var="loopObject" items="#{ApplicationBean.objectList}">
    <ui:include src="anotherTemplate.xhtml">
        <ui:param name="firstParam" 
            value="#{ApplicationBean.initForOtherTemplate(loopObject)}" />
    </ui:include>
</c:forEach>

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks! It make a lot of sense, but I'm running into other problems. If I try this with a list of one object, all is well. As soon as I try two objects or more, I get a NullPointerException. I'm having a hell of a time figuring out what's null. – Mr. Lance E Sloan Aug 06 '12 at 19:44
  • Having the stacktrace would be helpful as that gives a detailed insight as to what's happening in the code. – BalusC Aug 06 '12 at 19:52
  • I think I'm getting close. I've got it narrowed down, at least. I found that the null pointer exception was happening in the method I call in the `ui:param` tag. It seems that some of the other code in the app is changing `objectList` in the middle of the loop. I need to find out where and why. The strange thing is that this problem never appeared until after I changed `ui:repeat` for `c:forEach`. – Mr. Lance E Sloan Aug 06 '12 at 20:32
  • I had given your answer the green check-mark earlier, just after I resolved my null pointer problem. However, after I re-enabled some code that I had disabled during debugging, I find that the method `initForOtherTemplate` is **not** being executed when the parameter is assigned. `anotherTemplate.xhtml` **still** needs to do something with the parameter before the method will be executed. That's what I was hoping to avoid. – Mr. Lance E Sloan Aug 07 '12 at 18:20