JSF. Convert value for h:commandLink display - jsf

I am passing Status object to h:commandLink value. So it is displayed on the page. The problem is, displayed string is
packages.entity.Status#db2674c8.
I created converter for Status with annotation
#FacesConverter(forClass = Status.class, value = "statusConverter")
but it doesn't work. I tried to explicitly set it:
<h:commandLink value="#{result.status}" action="/view">
<f:converter converterId="statusConverter" />
</h:commandLink>
Then I got an error: /search-form.xhtml #57,58 <f:converter> Parent not an instance of ValueHolder: javax.faces.component.html.HtmlCommandLink#53e387f3
which is quite true, h:commandLink is not ValueHolder. Is there some way to convert value for h:commandLink?

Interesting, I'd intuitively expect it to work here, but the UICommand does indeed not extend UIOutput (while the UIInput does). It's maybe worth an enhancement request to JSF boys.
You can go around this issue by displaying it using <h:outputText>.
<h:commandLink action="/view">
<h:outputText value="#{result.status}">
<f:converter converterId="statusConverter" />
</h:outputText>
</h:commandLink>
Or just without explicit <f:converter> since you already have a forClass=Status.class
<h:commandLink action="/view">
<h:outputText value="#{result.status}" />
</h:commandLink>

Converters can not be attached to command components (h:commandLink, h:commandButton)
You could create a composite component or use a method in your backing bean for that.

As you pointed out an h:commandLink is not a ValueHolder so it does not support a converter. The value attribute actually dictates the text that is displayed.
Converters are used to convert a value that is an Object into a String for representation in html and then on the flip side to convert that String back into an instance of an object.
In your example I'm guessing that result.status is an object which you'd like to convert to a string? If so you may just need to reference an actual String attribute of the object, like:
<h:commandLink value="#{result.status.statusMessage}" action="/view" />

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.

commandLink with ui:repeat in composite component

I am trying to use links in a composite component, my links are in a simple array and do not belong to any managed bean.
<composite:interface>
<composite:attribute name="links" required="true" />
</composite:interface>
<composite:implementation>
<ui:param name="linksSplit" value="#{fn:split(cc.attrs.links, ',')}" />
<ui:repeat var="link" value="#{linksSplit}" >
<h:commandLink value="#{option}" action="#{link}" />
</ui:repeat>
</composite:implementation>
I am getting the following error :
Identity '#{link}' does not reference a MethodExpression instance, returned type: java.lang.String
We are supposed to use String or bean methods in EL expression but I don't understand why we cannot evaluate a parameter which is a String (link in my case).
If I put a real String referenced in my faces-config, it works
<h:commandLink value="#{option}" action="#navigate" />
If you have an explanation or a workaround to get my link working, it would be great
When specifying an EL expression in <h:commandLink action>, it's interpreted as a method expression returning a String (or void if you don't want to navigate). See also the tag documentation:
Name action
Type javax.el.MethodExpression
(signature must match java.lang.Object action())
Description MethodExpression representing the application action to invoke when this component is activated by the user. The expression must evaluate to a public method that takes no parameters, and returns an Object (the toString() of which is called to derive the logical outcome) which is passed to the NavigationHandler for this application.
Given the fact that you seem to want pure page-to-page navigation links, you're actually going in the wrong direction as to using <h:commandLink> for that. You should instead be using <h:link> for that. It generates SEO-friendly and bookmarkable GET links instead of a piece of JavaScript which submits a parent POST form.
<ui:repeat var="link" value="#{linksSplit}" >
<h:link value="#{option}" outcome="#{link}" />
</ui:repeat>
See also:
When should I use h:outputLink instead of h:commandLink?
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Note that this all has nothing to do with composite components. You'd have had exactly the same problem when using this in a normal page.

How to get p:inputText value and set it as f:param value?

I have an xhtml page as follows
<p:inputText id="inputFilterKey" name="inputFilterKey" value="#{key}" />
<p:commandButton id="filterByKey" action="searchByKey" value="Search" ajax="false">
<f:param name="filterKey" value=? />
</p:commandButton>
The parameter 'filterKey' should have the value which is provided by user in the inputText. Value '#{key}' is the flow scope variable which is defined in spring webflow. That is, it is not taken from a back bean. How should I get the value of the inputText? Here is the flow definition in case it is need.
<transition on="searchByKey" to="editTexts" >
<set name="flowScope.key" value="requestParameters.filterKey"/>
<evaluate expression="textManager.searchByKey(key)" result="viewScope.textsByKey" result-type="dataModel"/>
</transition>
Thanks
That isn't possible. The <f:param value> is evaluated when the form is rendered/displayed, not when the form is submitted/processed.
I'm not familiar with Spring Webflow, but this is IMO a really strange design. You might want to confirm with the SWF guys if you're doing things the right way. Perhaps you should rather inject the SWF variable as a managed bean property during its construction/initialization or something?
Anyway, there are ways to get the submitted value without binding the input component's value to a managed bean property. One of them is just getting it straight from the request parameter map by #ManagedProperty:
#ManagedProperty("#{param['formId:inputFilterKey']}")
private String key; // +setter
or when the managed bean has a broader scope than the request scope:
String key = externalContext.getRequestParameterMap().get("formId:inputFilterKey");
The formId:inputFilterKey is just the name of the generated HTML <input> element representation of the <p:inputText> component.

How to access object methods using dataTable in JSF?

I'm working with LastFM api, let's say I have a class called Artist which I call in this dataTable:
<h:dataTable var="artist" value="#{personEAO.topArtists}" >
<h:column>Artist : #{artist.name} </h:column>
</h:dataTable>
Artit have a field which refers to his picture :
artist.getImageURL(ImageSize.LARGE)
Which works fine, but how do I call this method in my jsf page using dataTable ?
I searched around for Javadocs, but I couldn't find them anywhere. The answer depends on what kind of constant ImageSize.LARGE is.
If ImageSize is an enum, just do:
<h:graphicImage value="#{artist.getImageURL('LARGE')}" />
But if it isn't and is thus a public static constant, then one of the ways is to wrap it in some helper bean which returns exactly that:
<h:graphicImage value="#{artist.getImageURL(someHelperBean.ImageSize_LARGE)}" />
I of course assume that your environment supports EL 2.2.

f:setPropertyActionListener sets null value instead of intended value

My view is:
<h:commandLink value="BookFlight" action="#{bookSeatController.doLoginOrCC}">
<f:setPropertyActionListener target="#{bookSeatController.flightNumber}"
value="#{flightInfoController.flight.number}" />
</h:commandLink>
My setter is:
public void setFlightNumber(String flightNumber) {
this.flightNumber = flightNumber;
}
When I use the debugger I get a flightNumber of null in the setter. However, if I change the view to the following:
<h:commandLink value="BookFlight" action="#{bookSeatController.doLoginOrCC}">
<f:setPropertyActionListener target="#{bookSeatController.flightNumber}"
value="122334" />
</h:commandLink>
The flightNumber property is set to 122334. How is this caused and how can I solve it to set the intended value instead of null?
If the #{flightInfoController.flight.number} is request scoped, then it has to preserve exactly the same flight in during the request of processing the form submit as it was during the request of displaying the form. This has to happen in the bean's (post)constructor.
If that is not an option, because it depends on some request based variables, then your best bet is to put the bean in the view scope instead (I however still assume that your bean is properly designed that it doesn't do any business/preloading job in getters).
If putting the bean in the view scope is in turn not an option, then you'd need to pass it as a fullworthy request parameter instead. You can do that by <f:param>.
<h:commandLink value="BookFlight" action="#{bookSeatController.doLoginOrCC}">
<f:param name="flightNumber" value="#{flightInfoController.flight.number}" />
</h:commandLink>
You can let JSF set it by #ManagedProperty in the BookSeatController or by <f:viewParam> in the current view.
See also:
How can I pass selected row to commandLink inside dataTable?
ViewParam vs #ManagedProperty(value = "#{param.id}")
If it's working when assigning "122334" but when assigning flightInfoController.flight.number it's "null" and since you are not receiving any exception, then it means probably your flightInfoController is not properly initialized (regarding it's field flight and hence number in the flight).
Just make sure the bean is properly initialized (or update your OP with the bean code).

Resources