Access iteration variable in passthrough attribute? - jsf

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

Related

JSF PrimeFaces rendering components

Let's say I have a simple method that, like this:
public String test()
{
return "hello";
}
Now let's say I have the following PrimeFace component:
<p:fieldset styleClass="containers" rendered="#{controller.test()}">
<h2>Testing</h2>
<p:outputLabel for="test" value="Test" />
<p:inputText id="test" />
</p:fieldset>
The method above returns "hello". I would like to dynamically show and hide that fieldSet by comparing the returned value of that method to a field of one of my beans. For instance, on the rendered parameter, I would like to do something like: controller.test() != "some variable" which would return true or false. Am I allow to do that? If not, what is the way of doing it?
Basically the goal is to dynamically show and hide some container by comparing the returned value of a method with a bean property.
Look Like you misunderstood rendered
The rendered Attribute
A component tag uses a Boolean EL expression, along with the rendered
attribute, to determine whether or not the component will be rendered.
If you will check above definition you will know what exactly is the use of this attribute.
More you can see below
The rendered attribute which uses Boolean EL expression indicates
whether a component is currently visible or not. The property is
useful when you want to hide components for specific users or based on
condition. The default value of rendered attribute is true.
<h:outputText value=”mapping” rendered=”Boolean EL expression” />
For example, the commandLink component in the following section of a page is not rendered if the cart contains no items:
<h:commandLink id="check"
...
rendered="#{cart.numberOfItems > 0}">
<h:outputText
value="#{bundle.CartCheck}"/>
</h:commandLink>
With your concrete problem you can do like this
Make a String variable call value
Now create get/set methods for above variable
Now in your test method you can add
public void test(){
value="hello";
}
Bur remember you have call test() method of page load
Now in your Xhtml or Jsf or Jsp page
rendered="#{controller.value != 'hello'}"
Or better way create a Boolean variable and do all the magic of hide and show the component

Primefaces won't call setter on ManagedBean

I'm in the middle of building a web application (Spring 4, Primefaces 5.1 and JPA).
While one of my xhtml-pages has no problems working with its ManagedBean, the others won't really work. To be more specific, the UI input components (like inputText) won't call any setters from the managed Beans.
I've already run my Tomcat (8) on debug mode, which showed me that the page calls the get-methods, but no set-methods at all. If I try to persist something, all values are null (or 0, in case of an int-value). (it even persists an object into the database with all values null, though I have declared some #NotNull constraints which should be taken to the database configuration via JPA)
So, question is: How can I make my inputFields work with the fields of my ManagedBean? (Eclipse also shows me in the editor, that theoretically, the connection to the fields is there, it knows the managed bean, the field and the get-/set-methods)
SoftwareManagedBean.java
#ManagedBean(name = "swmb")
#ViewScoped
public class SoftwareManagedBean extends AssetManagedBean implements
Serializable {
private String bezeichnung;
private Software neueSoftware;
// +some more private fields, every single one with its get-/set-method
#Override
public String getBezeichnung() {
return super.getBezeichnung();
}
#Override
public void setBezeichnung(final String bezeichnung) {
super.setBezeichnung(bezeichnung);
}
//instantiante the field "neueSoftware"
public void createEmptySoftware(){
if(neueSoftware != null)
return;
this.neueSoftware = new Software();
}
//Persist Software with values from inputFields
public void addSoftware() {
createEmptySoftware();
neueSoftware.setBezeichnung(getBezeichnung());
softwareService.addSoftware(neueSoftware);
//...
neueSoftware = null;
}
viewSoftware.xhtml
<h:body>
<p:dialog header="Neue Software anlegen" widgetVar="SwNeuDialog" width="60%"
closeOnEscape="true" draggable="false" resizable="false" position="center">
<h:form id="SwDlgForm">
<h:panelGrid columns="3" border="0" >
<p:outputLabel for="swBezeichnung" value="Bezeichnung: " />
<p:inputText id="swBezeichnung" value="#{swmb.bezeichnung}"
label="Bezeichnung" required="true" />
<f:verbatim/>
<p:outputLabel for="swKategorie" value="Kategorie: " />
<p:selectOneMenu id="swKategorie" value="#{swmb.kategorie}" label="Kategorie" required="true" >
<f:selectItem itemLabel="Kategorie wählen" value="#{null}" noSelectionOption="true"/>
<f:selectItems value="#{swmb.kategorieListe}" var="kat" itemLabel="#{kat.bezeichnung}" itemValue="#{kat}"/>
</p:selectOneMenu>
<p:commandButton value="neue Kategorie hinzufügen" />
<!-- + some more input fields -->
<p:commandButton value="Speichern" action="#{swmb.addSoftware()}" onclick="PF('SwNeuDialog').hide()" resetValues="true" process="#this"/>
<p:commandButton value="Abbrechen" onclick="PF('SwNeuDialog').hide()" resetValues="true" process="#this"/>
</h:panelGrid>
</h:form>
</p:dialog>
<!-- dataTable -->
</h:body>
AssetManagedBean.java
#ManagedBean
public abstract class AssetManagedBean {
//name of the hard-/software
private String bezeichnung;
//+ some more fields with get-/set methods
public String getBezeichnung() {
return bezeichnung;
}
public void setBezeichnung(String bezeichnung) {
this.bezeichnung = bezeichnung;
}
I hope that code is sufficient to see the problem, since the rest of the code follows the same structure. I think, the problem could lie within the xhtml file, but I don't see where or why. I've got the SpringBeanFacesELResolver (or however it is called), I've already looked through the code and compared it to another xhtml page and its Managed Bean, but there are no differences anymore. (though one is working, one not)
My debugger showed, how the working class/page (viewAdministration.xhtml) called the get-/set methods of the managed bean:
opening dialog window: get...(), set...()
clicking the commandButton to submit/persist: get() (old Value), set() (new Value), get() (new Value)
Another get() (called by the add... method)
(+ another get() for the dataTable on the main page)
on viewSoftware.xhtml, it looks like this:
opening dialog window: get()
clicking the commandButton to submit/persist:
another get() called by the addSoftware method
As you can see, when I try to submit, there is no set or get.
So, to summarize:
no setter called by trying to submit
the code on viewSoftware.xhtml and SoftwareManagedBean is similar to another, functioning ManagedBean + xhtml page (I've compared it again and again)
annotations in Managed Beans are the same (#ManagedBean, #ViewScoped)
the inputFields are inside a form (
I'm totally clueless, but I think it is some small mistake from my side that I can't just see.
I've searched through the web and especially stackoverflow, but all the problems and answers I've found couldn't help me finding what's wrong
Even without inheriting from a superclass it won't work (tried that out too)
I hope, you can help me. If this post is lacking some information, I'm sorry about that, I tried my best to not let this post grow too big and still get as much relevant information in it as possible.
So, I have found my mistake (or at least, I think I have). Only took me 2 weeks, but anyway...
I tried to test it out more specifically, wrote test classes and an xhtml page. Nothing worked (from simple Input over Dates to own classes).
The solution to this problem was to disable ajax on my commandButton (ajax="false"). When I tried to understand more of it, I realized that the commandButton opening the dialog window was nested in a facet inside a dataTable, and thus it had problems to properly set the values of the input fields.
So, thank you for your help. Maybe/hopefully this can or will help some other people later as well.
From the first glance at the code, without reading it whole, try putting process="SwDlgForm" on your command buttons, instead of process="#this". If that doesn't solve the problem, I'll read more carefully and try to help.

How do I implement a NamingContainer? All children get the same client ID

I try to write my own tree component. A tree node renders as a div containing child components of the tree component, for example:
<my:tree id="extendedTree"
value="#{controller.rootNode}"
var="node">
<h:outputText id="xxx" value="#{node.name}" />
<h:commandLink value="Test" actionListener="#{controller.nodeSelectionActionListener}" />
</my:tree>
So far, so good - everything works as expected, but the h:outputText gets the same id repeatedly.
So I had my component implement javax.faces.NamingController, overwriting getContainerClientId():
#Override
public String getContainerClientId(FacesContext context) {
String clientId = super.getClientId(context);
String containerClientId = clientId + ":" + index;
return containerClientId;
}
index is set and updated during iteration over the nodes. But getContainerClientId() is called only once for every children (not for every iteration and every children, as I would expect). That causes every child id to be prefixed with the same container id:
form:treeid:0:xxx
Same thing for overwriting getClientId().
What did I miss?
The answer is hidden in the bottom of chapter 3.1.6 of JSF 1.2 specification:
3.1.6 Client Identifiers
...
The value returned from this method must be the same throughout
the lifetime of the component instance unless setId() is called, in which case it will be
recalculated by the next call to getClientId().
In other words, the outcome of getClientId() is by default cached by the JSF component as implemented in UIComponentBase#getClientId() (see also the nullcheck at line 275 as it is in Mojarra 1.2_15) and this cache is resetted when the UIComponentBase#setId() is called (see also line 358 as it is in Mojarra 1.2_15). As long as you don't reset the cached client ID, it will return the same value on every getClientId() call.
So, while rendering the children in encodeChildren() implementation of your component or the renderer which shall most probably look like this,
for (UIComponent child : getChildren()) {
child.encodeAll(context);
}
you should for every child be calling UIComponent#setId() with the outcome of UIComponent#getId() to reset the internally cached client ID before encoding the child:
for (UIComponent child : getChildren()) {
child.setId(child.getId());
child.encodeAll(context);
}
The UIData class behind the <h:dataTable> implementation does that by the way also (see line 1382 as it is in Mojarra 1.2_15). Note that this is not JSF 1.x specific, the same applies on JSF 2.x as well (and also on UIRepeat class behind Facelets <ui:repeat>).
It's worth mentioning that if your component's children have children, then it may also be necessary to refresh their cached ids, too. With this mark-up, slightly adapted from the original:
<my:tree id="extendedTree"
value="#{controller.rootNode}"
var="node">
<h:panelGroup layout="block" id="nodeBlock">
<h:outputText id="xxx" value="#{node.name}" />
<h:commandLink value="Test" actionListener="#{controller.nodeSelectionActionListener}" />
</h:panelGroup>
</my:tree>
The id for the <panelGroup> comes out OK after applying BalusC's fix above but all the sub-components come out with 0 in the iterator.
To fix that, iterate through all the levels of children too and refresh their cached ids. So:
child.setId(child.getId()); becomes uncacheId(child); where the uncacheId function is defined:
private void uncacheId(UIComponent el) {
el.setId(el.getId());
el.getChildren().forEach(this::uncacheId);
}
That may be obvious but it took me a while to figure out, so ...
h:outputText id gives you same as you didn't make it dynamic. You can create it like:
<h:outputText id="xxx_#{node.id}" value="#{node.name}" />
Assume node has an 'id' attribute which is an unique.

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>

JSF Temporary Variable

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}"/>

Resources