EL expression in c:set gets called multiple times instead of its return value being used - jsf

I have an Action in request scope. The return value of one of its methods is passed around to a custom facelet tag. This tag then extracts several attributes of the returned object and displays them. The problem is the EL expression which has the method call on the Action is called for every evaluation of the attribute of the returned object. I will put the relevent pieces of code here.
some.xhtml
<ui:include src="someOther.xhtml">
<ui:param name="profileUri" value="#{param['relateToProfile']}"/>
<ui:param name="qualifier" value="#{param['qualifier']}"/>
<ui:param name="cellStyleClass" value="#{param['cellStyle']}"/>
</ui:include>
someOther.xhtml (approach 1) Note that ProfileAction is in #RequestScoped
<tenui:entityCard profileEntity="#{profileAction.getProfileMetadata(profileUri)}"
qualifier="#{qualifier}"
cellStyleClass="#{cellStyleClass}"/>
enityCard.xhtml(facelet custom tag)
<ui:fragment rendered="#{profileEntity.featured}">...
<tenui:gridCell id="#{profileEntity.profileId}#{qualifier}" ...
<tenui:metaunit ..content="#{profileEntity.getMeta('memberName')}"
href="/#{profileEntity.profileDisplayUri}"
hrefStyleClass="a-styled grid-cell-name"/>
.....
...several other EL expressions including #{profileEntity.xxx}
The problem is #{profileAction.getProfileMetadata(profileUri)} is being called for every attribute evaluation in entityCard.xhtml Then, I thought I would save the return value of method call in a c:set var(approach 2 as noted below) but it doesn't help.
someOther.xhtml (approach 2)
<c:set var="profileMetadata"
value="#{profileAction.getProfileMetadata(profileUri)}"/>
<tenui:entityCard profileEntity="#{profielMetadata}"
qualifier="#{qualifier}"
cellStyleClass="#{cellStyleClass}"/>
The action method calls a Stored proc which is quite expensive and the returned object has over 20 attributes that get evaluated in ELs in entityCard.xhtml.
I also tried another approach with resolving teh value at ui:param itself by calling the action method directly, but of no avail at all. The problem remained.
Can someone point to what could I be doing wrong? Or, how I could avoid the multiple calls to profileAction.getProfileMetadata call?

You need to set the scope attribute of <c:set> to one of the desired scopes, request, view, session or application. Otherwise it defaults to none.
Assuming that you want it to be request, this should do:
<c:set var="profileMetadata" scope="request"
value="#{profileAction.getProfileMetadata(profileUri)}" />

Related

Setting f:setPropertyActionListener value with a f:param value

I'm trying to use the setPropertyActionListener tag to set a value in my backing bean. However, it doesn't work as I expected.
Context: userService is an instance of my backing bean, which contains an int member, reqID. This, in turn, is the key to a map of objects that belong to a class called User. I'm trying to create a page that will list all instances of User, and provide a button to visit a separate view that shows that particular User's information. To do this, I'm attempting to set userService.reqID to the id of the chosen User so it can generate a reference to that user for the next view (which is done in the call userService.toUserInfo).
If I use the xhtml snippet below:
<ui:define name="content">
<h:form>
<h:panelGrid>
<ui:repeat value="#{userService.UserList.getUserList()}" var="user">
<li>
<h:outputText value="#{user.name}" />
<h:commandButton value="View details of #{user.name}" action="#{userService.toUserInfo}">
<f:param name="id" value="#{user.id}" />
<f:setPropertyActionListener target="#{userService.reqID}" value="#{id}"/>
</h:commandButton>
</li>
</ui:repeat>
</h:panelGrid>
</h:form>
</ui:define>
The tag does not appear to evaluate id correctly and I get a Null Pointer Exception.
Earlier, I tried changing my setPropertyActionListenerTag so it read out as:
<f:setPropertyActionListener target="#{userService.reqID}" value="id"/>
which gave me an error, because the tag was sending the string "id" as opposed to the int value of the parameter.
Is there some way to force f:setPropertyActionListener to evaluate the expression under value? Or is there another tag that will allow me to do this?
Also, is ui:param used appropriately here?
The <f:param> (and <ui:param>) doesn't work that way. The <f:param> is intented to add HTTP request parameters to outcome of <h:xxxLink> and <h:xxxButton> components, and to parameterize the message format in <h:outputFormat>. The <ui:param> is intented to pass Facelet context parameters to <ui:include>, <ui:decorate> and <ui:define>. Mojarra had the bug that it also behaves like <c:set> without a scope. This is not the intented usage.
Just use <c:set> without a scope if it's absolutely necessary to "alias" a (long) EL expression.
<c:set var="id" value="#{user.id}" />
Put it outside the <h:commandLink> though. Also in this construct, it's kind of weird. It doesn't make the code better. I'd just leave out it.
<f:setPropertyActionListener ... value="#{user.id}" />
See also:
Setting ui:param conditionally
what is the scope of <ui:param> in JSF?
Defining and reusing an EL variable in JSF page
Unrelated to the concrete problem, if you're using EL 2.2 (as you're using JSF 2.2, you undoubtedly are as it requires a minimum of Servlet 3.0, which goes hand in hand with EL 2.2), then just pass it as bean action method argument without <f:setPropertyActionListener> mess. See also a.o. Invoke direct methods or methods with arguments / variables / parameters in EL and How can I pass selected row to commandLink inside dataTable?
<h:commandButton ... action="#{userService.toUserInfo(user.id)}">
On again another unrelated note, such a "View user" or "Edit user" request is usually idempotent. You'd better use <h:link> (yes, with <f:param>) for this. See also a.o. Creating master-detail pages for entities, how to link them and which bean scope to choose and How to navigate in JSF? How to make URL reflect current page (and not previous one).
Oh, that <h:panelGrid> around the <ui:repeat><li> doesn't make sense in HTML perspective. Get rid of it and use <ul> instead. See also HTMLDog HTML Beginner tutorial.

How to invoke a managed bean action method in on* attribute of a JSF component

I'd like to invoke a managed bean action method in an on* attribute. In my particular case I need to logout an user if the user is idle for 3 minutes as below:
<p:idleMonitor onidle="#{mybean.processTimeOut()}" timeout="180000" />
However, the managed bean action method is immediately invoked as the page loads. How is this caused and how can I solve it?
Like as all other on* attributes on all JSF components, the onidle attribute must represent a JavaScript callback, not a JSF backing bean action method. Any EL expressions in on* attributes would be evaluated immediately as String value expressions during generating the HTML output in expectation that they print (part of) JavaScript code.
It's exactly like as if you're doing <h:outputText value="#{mybean.processTimeout()}">. If you had removed the parentheses (), you'd have faced a PropertyNotFoundException which was also a hint at its own of it being evaluated as a value expression instead of a method expression.
In order to invoke a JSF backing bean method using JavaScript, you need an additional <p:remoteCommand>.
<p:idleMonitor onidle="processTimeout()" timeout="180000" />
<p:remoteCommand name="processTimeout" action="#{mybean.processTimeOut}" />
If you're not on PrimeFaces, head to the alternatives posted in this related answer: How to invoke a JSF managed bean on a HTML DOM event using native JavaScript?

PropertyNotFoundException EL Expressions not resolving?

I have code similar to this in my facelet...
<c:foreach items=#{myBean.listOfA} var="a">
<c:foreach items=#{myBean.listOfB} var="b">
<c:set var="aName" value="#{a.name}">
<c:set var="component" value="#{b.associatedComponent(aName)}">//this wont resolve
//do stuff with component
</c:foreach>
</c:foreach>
myBean is a session scoped bean. Both A and B classes are maintained in lists inside the bean but are just model objects not managed beans.
That being said the method b.associatedComponent(a) I am assuming should resolve to b.getAssociatedComponent(A a) which I have checked many times. All methods are public. What can I do to make the method accessible to EL?
No matter what I try I get a PropertyNotFoundException on associatedComponent.
P.S. I also tried moving the method to the bean like this...
<c:set var="component" value="#{myBean.associatedComponent(b, aName)}">
This also does not work and throws the same PropertyNotFoundException.
This will not work, as the c:set tag expects a ValueExpression for the value attribute.
This means that you are only able to use properties - not methods.
If you use arguments like here #{b.associatedComponent(aName)} then what you have is a method call, not a property call. This however is a valid MethodExpression and can be used in places like the action attribute of a command link.
See the API documentation and spec for more details on this.

commandLink is not fired in a page with a param in its URI

When I call a method in a page with a param in its URI, the method is not invoked unless I pass the parameters of the uri again. For example if I have:
http://maywebsite/myapp/mypage.xhtml?mykey=myvalue
This method results in error (obviously because it renders the page again without params, but the method foo is never invoked):
<h:commandLink value="Do Action" actionListener="#{mybean.foo}"/>
So I added an ajax to only update the component, but the button is not getting fired:
<h:commandLink value="Do Action" actionListener="#{mybean.foo}">
<f:ajax render="somecomponent"/>
</h:commandLink>
When I passed the param values again, the button invokes the method just fine:
<h:commandLink value="Do Action" actionListener="#{mybean.foo}">
<f:param name="mykey" value="myvalue"/>
<f:ajax render="somecomponent"/>
</h:commandLink>
However, this button is included (ui:include) in many pages with different param keys and values. How can I invoke the method without passing the param values?
Im using glassfish 3.1.2, jsf 2.0
Apparently the bean is request scoped and the parameter plays a role in the way how the command link is rendered (e.g. by the rendered attribute on one of its parent components, or by a dynamic include of the template containing the command link).
All those conditions are namely re-evaluated during apply request values phase of the form submit. The developer has to make sure that all those conditions are exactly the same as when the form was presented to the enduser. So, when the bean is request scoped and the parameter is absent, then the command link appears as non-rendered in the component tree and this way its action won't be invoked.
Putting the bean in the view scope is the easiest way to fix this (unless you're using a dynamic <ui:include>, this is then more complicated, you'd need to turn off partial state saving for the particular view).
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 5

Jsf dynamically created actionListener parameter

I have a custom facelet component (custom tag) but there is one thing that currently annoys me a lot. I'm trying to pass an action listener as a parameter to the custom tag, in order to make it more dynamic and just doesn't work.
Environment: JSF 1.2, IceFaces 1.8
Here's the way I'm trying to achieve this:
the command link uses the passed parameter 'paginationController'
<h:commandLink id="#{id}-link-three" value="click"
actionListener="#{paginationController.paginationLinkClicked}" />
the parameter 'paginationController' is passed like this:
paginationController="rowSelectController"
when I click the link, there's what I receive:
Received 'javax.el.MethodNotFoundException' when invoking action listener
'#{paginationController.paginationLinkClicked}' for component 'entity-list-apps-link-three'
2011-02-22 12:49:47,803 SEVERE [javax.faces.event] (http-127.0.0.1-8080-4)
javax.el.MethodNotFoundException: /WEB-INF/jsf/common/components/facelets
/applicationList.xhtml #107,71 actionListener="#{paginationController.paginationLinkClicked}":
Method not found: rowSelectController.paginationLinkClicked(javax.faces.event.ActionEvent)
So it seems that it successfully resolves the bean name to rowSelectController, but it complaints it can't find the method, and the method IS there!
One more thing, if I replace the parameter with the correct controller's name, it just works! Any ideas?
Because paginationController is variable you need to use the [] syntax here. Assuming paginationLinkClicked is a fixed method name:
<h:commandLink actionListener="#{paginationController['paginationLinkClicked']}" />
If paginationLinkClicked would also be variable, you need to omit the single quotes:
<h:commandLink actionListener="#{paginationController[paginationLinkClicked]}" />

Resources