JSF Temporary Variable - jsf

Is there a way to temporarily save the value of calcuation in a JSF page?
I want to do the following without calculating twice:
<h:outputText value="#{complexModel.currencyAmount.currency}">
<h:outputText value="#{complexModel.currencyAmount.amount}">
I've tried using the alias bean but I get an error saying java.lang.IllegalArgumentException - row is unavailable.
e.g.
<t:aliasBean id="bean" alias="#{bean}" value="#{complexModel.currencyAmount}">
<h:outputText value="#{bean.currency}">
<h:outputText value="#{bean.amount}">
</t:aliasBean>
Thanks.

Two ways (at least):
Using lazy-init field of your complexModel. something like:
private Currency currencyAmount;
public Currency getCurrencyAmount() {
if (currencyAmount == null) {
currencyAmount = calculateCurrencyAmount();
}
return currencyAmount;
}
Using the JSTL <c:set> tag:
(the namespace first)
xmlns:c="http://java.sun.com/jstl/core"
then
<c:set var="varName" value="#{complexModel.currencyAmount}" />
And then the calculated value will be accessible through #{varName}.

In JSF 2.0,you can use <ui:param/> which it's powerful.
<ui:param name="foo" value="#{zhangsan.foo}"/>

Related

Access iteration variable in passthrough attribute?

I'm wondering why I get a NullPointerException on the following:
<h:dataTable value="#{testBean.entities}" var="d">
<h:column p:data-order="#{d.modifiedOn}">
<f:facet name="header">Date</f:facet>
<h:outputText value="#{d.modifiedOn}">
<f:convertDateTime pattern="#{msg.datePattern}" />
</h:outputText>
</h:column>
</h:dataTable>
Where the p namespace is xmlns:p="http://xmlns.jcp.org/jsf/passthrough", modifiedOn is a (non-null) Date field.
The exception looks like this:
Caused by: java.lang.NullPointerException
at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.getAttributeValue(HtmlResponseWriter.java:1211)
at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.flushAttributes(HtmlResponseWriter.java:1171)
at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.closeStartIfNecessary(HtmlResponseWriter.java:1113)
at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.writeText(HtmlResponseWriter.java:936)
at com.sun.faces.facelets.el.ELText$ELTextVariable.writeText(ELText.java:240)
at com.sun.faces.facelets.compiler.TextInstruction.write(TextInstruction.java:85)
at com.sun.faces.facelets.compiler.UIInstructions.encodeBegin(UIInstructions.java:82)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:302)
at com.sun.faces.renderkit.html_basic.TableRenderer.renderHeader(TableRenderer.java:339)
However, use of #{d.modifiedOn} is fine in any non-passthrough attributes like headerClass. As is any value not referencing the iteration variable in a passthrough attribute.
My question is: is this intended?
This is using Wildfly-8.0.0.Final's JSF implementation, version 2.2.5-jbossorg-3.
In case anyone's wondering: I'm trying to support datatables orthogonal data attributes to enhance my tables.
Edited to add: I've also tried, but got the same Exception, with adding the following to the <h:column>:
<f:passThroughAttribute name="data-order" value="#{d.modifiedOn}" />
This is apparently caused by JSF trying to render this attribute on the header where rowIndex is -1 and iteration data is not yet present. So there's an hack solution to my specific problem:
<h:column p:data-order="#{empty d or empty d.modifiedOn ? '' : d.modifiedOn.time}">
...
</h:column>
Which still renders an empty/useless data-order attribute into my thead.
Further testing:
More testing has confirmed my suspicion that this only occurs with a <f:facet name="header"> on any column (doesn't have to be the one with the attribute). Leaving those out defeats my goal of using the table as a base for datatables since that requires a well-formed table with a <thead>.
Is this intended? Definitelly it's not. Furthermore, here is the definition of a passthrough attribute according to the JSF 2.2 specification:
Pass Through Attributes
For any given JSF component tag in a view, the set of available attributes that component supports is determined by a
combination of the UIComponentand Renderer for that tag. In some cases the value of the attribute is interpreted by
the UIComponentor Renderer(for example, the columns attribute of h:panelGrid) and in others the value is
passed straight through to the user agent (for example, the lang attribute of h:inputText).
In both cases, the
UIComponent/Renderer has a priori knowledge of the set of allowable attributes. Pass Through Attributes allows
the view author to list arbitrary name value pairs that are passed straight through to the user agent without interpretation
by the UIComponent/Renderer. This behavior is normatively specified in the “Rendering Pass Through Attributes”
section of the overview of the standard HTML_BASIC render kit.
So the only functionality of that kind of attributes is to notify the renderer that it must process the value for the attribute, but that it's not tied to any of the UIComponent features, so must end up at the final output.
I have performed a test with Tomcat 7 + Mojarra 2.2.6 and it perfectly works for me. HTML elements get rendered in the DOM tree with their data-order attribute, with the given date. That's the test case I've used:
<html xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
<h:head />
<h:body>
<h:dataTable value="#{bean.entities}" var="entity">
<h:column p:data-order="#{entity.modifiedOn}">
#{entity.name}
</h:column>
</h:dataTable>
<h:messages />
</h:body>
</html>
#ManagedBean
#ViewScoped
public class Bean {
public class Entity {
private String name;
private Date modifiedOn;
public Entity(String name, Date modifiedOn) {
this.name = name;
this.modifiedOn = modifiedOn;
}
public Date getModifiedOn() {
return modifiedOn;
}
public String getName() {
return name;
}
}
/**
* Create a List of entities with dates differing from now to now + 2 days
*/
public List<Entity> entities = Arrays.asList(
new Entity("name1", new Date()), new Entity("name2", new Date(
new Date().getTime() + (1000 * 60 * 60 * 24))), new Entity(
"name0", new Date(new Date().getTime()
+ (1000 * 60 * 60 * 48))));
public List<Entity> getEntities() {
return entities;
}
}
This means it's a bug in your JSF implementation (implementation which I didn't know about BTW). Your best is opening a bug track unless the authors have already fixed it in a later version.
Edit
The rendering fails with the given error when there's an f:facet for header or footer involved for the datatable. Apparently, Mojarra tries to evaluate the passthrough value for the facet before building the iterator, that causes it to fail as the variable is not yet available. Opened an issue.
See also:
JSF 2.2 specification

JSF Access viewParams from within a component

I have a component to display a entry. This component can appear multiple times within the same page, and therefore just takes an entity, passes it to the component's backing bean where all the magic happens.
The component's appearence can be affected by various parameters.
Now, it is a requirement, that no matter if I have 1 or 10 of those components, the "same" view-state should be recoverable using a deeplink.
Let's say my component looks like this:
<composite:interface componentType="itemView" >
<composite:attribute name="item" required="true" shortDescription="The item to display."/>
<composite:attribute name="urlPrefix" required="true" />
</composite:interface>
<composite:implementation>
<f:event type="preRenderComponent" listener="#{cc.init}" />
....
</composite:implementation>
it has a corresponding backing Bean, I can access. (leaving this out, as its not required)
Now, these components are embedded on a page, that looks like this:
<f:metadata>
<f:viewParam name="itemType"
value="#{listController.itemType}"></f:viewParam>
</f:metadata>
<ui:define name="content">
<!-- multiple occurences, ui repeat over list of items -->
<my:itemView item="#{currentItem}" urlPrefix="#{listController.nextLetter}">
As already mentioned, the state of every item can be affect, and the changed state musst be accessible by a deeplink. So, my idea was, to use <h:link> inside the component, prefix every attribute and use includeViewParams="true", so that every (not defaulted) state is part of the url.
<composite:interface componentType="itemView" >
<composite:attribute name="item" required="true" shortDescription="The item to display."/>
<composite:attribute name="urlPrefix" required="true" />
</composite:interface>
<composite:implementation>
<f:event type="preRenderComponent" listener="#{cc.init}" />
<h:link includeViewParams="true">
<f:param name="#{cc.attrs.urlPrefix}size" value="#{cc.largerSize}" />
larger
</h:link>
....
</composite:implementation>
Just as for the "larger" link, there are upto 10 links, providing different options.
The problem: include-view-params does include all the view-params from within the same component, and its parent (itemType), but NOT from other components. So, basically changing the style of "one" component, will drop the parameters of another component.
Expected:
The url already looks like ?itemType=something&asize=5&abackground=green
Clicking the "larger" link on the b component will give me : ?itemType=something&asize=5&abackground=green&bsize=5
Whats happening:
After clicking the link, the url looks like: ?itemType=something&bsize=5
so the attributes for "a" are dropped.
I noticed, that if i extend the <f:metadata> object on the listing-page with something like <f:viewParam name="asize" /> the aSize parameter will be preserved, when clicking something from within "b" - but of course if i need to list all parameters like THAT, i dont need components, because i can not predict the number of items.
So, i tried to add this just right inside the component (like the f:event): <f:viewParam name="#{cc.urlPrefix}size" /> - but that did not work.
Any Ideas on that (or alternative solutions? Note: Saving the view States in the database, and simple using a "single" id to reference that state is basically bad, because that would cause a big amount of datapolution. The View-State needs to stay transient, but recoverable :0) )
Finally i found a solution. It's not the best, but it works at least ;)
I created a method on the component's bean, that will return a query string, containing all values ecxcept the one(s) passed as a paremeter.
So, whenever I want to change the parameter asize=5, i call this function with param size and append the new parameter (Function will take care of excluding this parameter only, when the prefi matches, so no other components style would be affected.):
public String buildUrlWithout(String keyString) {
List<String> keys = ConversionHelper.Explode(keyString, ",");
// get all parameters from the url.
String prefix = this.getAttributes().get("urlPrefix").toString();
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
Map<String, String> params = externalContext.getRequestParameterMap();
List<KeyValuePair<String, String>> result = new LinkedList<KeyValuePair<String, String>>();
outerfor: for (Map.Entry<String, String> kvp : params.entrySet()) {
for (String s : keys) {
if (kvp.getKey().equals(prefix + s)) {
continue outerfor;
}
}
// keep param
result.add(new KeyValuePair<String, String>(kvp.getKey(), kvp.getValue()));
}
String qStr = KeyValuePair.toQueryString(result);
return qStr;
}
KeyValuePair ist just a Helper-Class that behaves like a Map<S,T> but offers several functions like join(), split() etc.. (in this case toQueryString() is used)
Finally, from within a component, I use this like this:
<h:outputLink
value="#{cc.buildUrlWithout('size')}&#{cc.attrs.urlPrefix}size={cc.size +1}">
larger
</h:outputLink>
<h:outputLink
value="#{cc.buildUrlWithout('size')}&#{cc.attrs.urlPrefix}size={cc.size -1}">
smaller
</h:outputLink>
<h:outputLink
value="#{cc.buildUrlWithout('background,border')}&#{cc.attrs.urlPrefix}background=red&#{cc.attrs.urlPrefix}border=green">
red background + green border
</h:outputLink>

In JSF2.0, how do I write out cookies in facelets?

I know how to write out individual cookies from facelets:
JSF:
<h:outputText value="#{facesContext.externalContext.requestCookieMap['TESTCOOKIE'].value}" />
Outputs:
MyCookieValue
I have been able to write out map, but the output is not the values but a reference to the value.
JSF:
<h:outputText value="#{facesContext.externalContext.requestCookieMap}" />
Output:
{DEFAULTUSERNAME=javax.servlet.http.Cookie#36a236a2,
TESTCOOKIE=javax.servlet.http.Cookie#36b436b4,
JSESSIONID=javax.servlet.http.Cookie#36d836d8}
You don't need such a long value expression to access your cookies in JSF 2.0, there is an implicit object named cookie which references the cookie map and it's equivalent to facesContext.externalContext.requestCookieMap.
So, following code:
<h:outputText value="#{cookie['TESTCOOKIE'].value}" />
should output the same as:
<h:outputText value="#{facesContext.externalContext.requestCookieMap['TESTCOOKIE'].value}" />
Now, if you want to iterate through all of them, my recommendation is to use backing bean:
#RequestScoped
#ManagedBean(name = "triky")
public class TrikyBean {
public List getCookies() {
FacesContext context = FacesContext.getCurrentInstance();
Map cookieMap = context.getExternalContext().getRequestCookieMap();
return new ArrayList(cookieMap.values());
}
}
And then use it like this
<ui:repeat value="#{triky.cookies}" var="ck">
#{ck.name}: #{ck.value}<br/>
</ui:repeat>
Clarification: This comes from the fact that the <ui:repeat /> tag will only accept java.util.List in its value attribute, otherwise it will create its own ListModel with just one element inside. Besides, the collection given by the default implementation of the values() method in a java.util.Map is not a java.util.List but a java.util.Set, so, the <ui:repeat/> tag was using that set as the only element of its own list model and when iterating through that list model the number of elements was just one and none of them were actually cookies.
Maps have a values() method that returns a collection of all elements. I think you need a stronger EL engine than the default to do method invocation outside of getters to do that though, like JBoss EL or JUEL (both of which i strongly recommend for any java ee 6 project).
The alternative is doing method invocation in java and supplying a getter like this:
myBean.java
public Collection getCookies(){
return FacesContext.getCurrentInstance().getExternalContext().getRequestCookieMap().values();
}
And iterating over the collection in your markup
<ui:repeat value="#{myBean.cookies}" var="cookie">
<p>#{cookie.name}: #{cookie.value</p>
</ui:repeat>
Haven't tried this out but something similar will work. You might have to replace the Collection with a List, not sure if ui:repeat supports Collections.
EDIT: as per the comment below, you could try this:
<ui:repeat value="#{facesContext.externalContext.requestCookieMap.values()}" var="cookie">
<p>#{cookie.name}: #{cookie.value</p>
</ui:repeat>

Issue with f:setPropertyActionListener and h:dataTable

I am trying to pass two parameters i.e. (nomComposantARejouer, typeFileARejouer) to an action method (gestionnaireMessagesController.rejouerMessage) using the setPropertyActionListener (we use jsf 1.2).
Here is the relevant jsp code:
<h:column>
<h:columnHeaderFacet>
<h:columnHeader value="#{msgs['pilotage.coordinateur.libelle.rejouer']}" />
</h:columnHeaderFacet>
<h:commandLink id="rejouer"
value="#{msgs['pilotage.coordinateur.libelle.rejouer']}"
action="#{gestionnaireMessagesController.rejouerMessage}">
<f:setPropertyActionListener
target="#{gestionnaireMessagesController.nomComposantARejouer}"
value="#{gestionnaireMessagesController.nomComposant}" />
<f:setPropertyActionListener
target="#{gestionnaireMessagesController.typeFileARejouer}"
value="#{gestionnaireMessagesController.typeFile}" />
</h:commandLink>
</h:column>
However, I always get a NPE because both parameters are null when used in the action method:
public String rejouerMessage() {
log.debug("-->"+nomComposantARejouer);//null
ParamResultatMessagesDTO message= (ParamResultatMessagesDTO) messagesTableau.getRowData();
log.debug("MessageId: " + message.getMessageId());
try {
Pager p = Pager.getInstance();
ParamRejouerMessageDTO prm = new ParamRejouerMessageDTO();
prm.setMessageId(message.getMessageId());
prm.setFileGet(nomsFilesMap.get(nomComposantARejouer).get(typeFileARejouer));
prm.setFilePut(nomsFilesMap.get(nomComposantARejouer).get("TASKQ"));
RejouerMessageService serv = (RejouerMessageService) this.getService(ServiceCst.REJOUER_MESSAGE_SERVICE);
serv.rejouerMessage(prm);
} catch (BusinessException e) {
this.addMessage(e);
} catch (ServiceException e) {
this.addMessage(e);
}
return chargerPage(); // TODO Navigation case.
}
I am not sure what I am getting wrong. Can anyone please help?
FYI, the variables nomComposant and typeFile can be displayed without problem and are not null.
Also ignore the h:columnHeaderFacet tags. They are inhouse tags that I've renamed.
You're basically doing:
gestionnaireMessagesController.setNomComposantARejouer(gestionnaireMessagesController.getNomComposant());
gestionnaireMessagesController.setTypeFileARejouer(gestionnaireMessagesController.getTypeFile());
during the invoke action phase of the form submit. Both properties are in the same bean instance and copied shortly before action method is invoked. This makes no sense. You seem to be expecting that the value is evaluated during the request of displaying the form. You seem to be thinking that <f:setPropertyActionListener> sets a request parameter. This is not true. The <f:param> is the only which does that.
So, this should do,
<f:param
name="nomComposantARejouer"
value="#{gestionnaireMessagesController.nomComposant}" />
<f:param
name="typeFileARejouer"
value="#{gestionnaireMessagesController.typeFile}" />
in combination with the following on <managed-bean> of gestionnaireMessagesController in faces-conig.xml:
<managed-property>
<property-name>nomComposantARejouer</property-name>
<value>#{param.nomComposantARejouer}</value>
</managed-property>
<managed-property>
<property-name>typeFileARejouer</property-name>
<value>#{param.typeFileARejouer}</value>
</managed-property>
(you can if necessary remove ARejouer so that it reuses the same property)
I have often encountered this problem , it occours when there are two f:setPropertyActionListener.
You try in this way:
<h:commandLink id="rejouer"
value="#{msgs['pilotage.coordinateur.libelle.rejouer']}"
action="#{gestionnaireMessagesController.rejouerMessage}"> <f:param
name="nomComposant"
value="#{gestionnaireMessagesController.nomComposant}" />
<f:param name="typeFile"
value="#{gestionnaireMessagesController.typeFile}" />
</h:commandLink>
Read this link: http://www.coderanch.com/t/211274/JSF/java/Passing-param-commandLink

Defining and reusing an EL variable in JSF page

Is it possible to define variable and reuse the variable later in EL expressions ?
For example :
<h:inputText
value="#{myBean.data.something.very.long}"
rendered="#{myBean.data.something.very.long.showing}"
/>
What i have in mind is something like :
<!--
somehow define a variable here like :
myVar = #{myBean.data.something.very.long}
-->
<h:inputText
value="#{myVar}"
rendered="#{myVar.showing}"
/>
Any ideas ? Thank you !
You can use <c:set> for this:
<c:set var="myVar" value="#{myBean.data.something.very.long}" scope="request" />
This EL expression will then be evaluated once and stored in the request scope. Note that this works only when the value is available during view build time. If that's not the case, then you'd need to remove the scope attribtue so that it becomes a true "alias":
<c:set var="myVar" value="#{myBean.data.something.very.long}" />
Note thus that this does not cache the evaluated value in the request scope! It will be re-evaluated everytime.
Do NOT use <ui:param>. When not used in order to pass a parameter to the template as defined in <ui:composition> or <ui:decorate>, and thus in essence abusing it, then the behavior is unspecified and in fact it would be a bug in the JSF implementation being used if it were possible. This should never be relied upon. See also JSTL in JSF2 Facelets... makes sense?
Like any view in MVC, the page should be as simple as possible. If you want a shortcut, put the shortcut into the controller (the #ManagedBean or #Named bean).
Controller:
#Named
public MyBean
{
public Data getData()
{
return data;
}
public Foo getFooShortcut()
{
return data.getSomething().getVery().getLong();
]
}
View:
<h:inputText
value="#{myBean.fooShortcut}"
rendered="#{myBean.fooShortcut.showing}"
/>

Resources