Facelets EL Functions can save your day
In the last week I had several issues during the development of a jsf application that could be solved by creating few custom Facelets EL functions, let’s take a look on which scenarios Facelets EL functions helped me.
1. Simple loop over a range of numbers
JSF has a lot of components that can be used to iterate over a model and repeat its own child components several times, some of them are Tomahawk’s dataList and Richfaces repeat.
But in all of these components I couldn’t find a way to do a simple iteration on a range of numbers, like repeat all child components starting from 1 and stopping at n number of times.
Maybe you will say: “What about c:forEach? It can do this kind of iteration!”, oh yes, it can, and I tried to use it but it crashes when I pass EL expressions to both begin and end attributes, it only accepts hardcoded values.
In order to solve my issue, I created a custom Facelets EL function, this EL function takes a integer as argument that will be used to specify the size of a very simple List that will used on a Tomahawk’s dataList tag.
Look the code below:
ELFunctions.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
To use the EL function on your page you must register as Facelets EL function in the file below:
target.taglib.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?xml version="1.0"?> <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd"> <facelet-taglib> <namespace>http://www.druid.com.br/target</namespace> <function> <function-name>loopModel</function-name> <function-class> br.com.druid.target.util.ELFunctions </function-class> <function-signature> List loopModel(java.lang.Integer) </function-signature> </function> </facelet-taglib> |
And now you are ready to use it with any jsf repeater component:
wizardSteps.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <ui:composition xmlns:h="http://java.sun.com/jsf/html" xmlns:t="http://myfaces.apache.org/tomahawk" xmlns:target="http://www.druid.com.br/target" xmlns:ui="http://java.sun.com/jsf/facelets"> <div> <t:dataList first="1" rowIndexVar="step" value="#{target:loopModel(totalSteps)}"> <t:div> <h:graphicImage value="/status#{step}#{step eq currentStep ? 'b' : 'a'}.jpg"/> </t:div> </t:dataList> </div> </ui:composition> |
In the example above I realy need repeat the child components only n times and to get it a list with a limited size was created.
2. Converting a value passed to a f:param.
The are some circunstances where a h:outputFormat can help a lot on some ui data translation (e.g. returning a week day name from a number), but it need the number of week day to be passed as param, and for this I’m using a f:param.
But what if this param isn’t a integer? We need convert this data, right? I tried to figure out how can I do it by attaching a converter to a f:param, but I couldn’t find nothing about it anywhere.
How do I solved it? Simple, I just wrote another EL function that can takes any Object as an argument and then I returned a integer value of this object.
The code below shows how can I do it:
ELFunctions.java:
1 2 3 4 5 6 7 8 9 10 11 12 |
Now I must register the EL function in the same way as described previously:
target.taglib.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?xml version="1.0"?> <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd"> <facelet-taglib> <namespace>http://www.druid.com.br/target</namespace> <function> <function-name>converterParaNumero</function-name> <function-class> br.com.druid.target.util.ELFunctions </function-class> <function-signature> int convertToInteger(java.lang.Object) </function-signature> </function> </function> </facelet-taglib> |
Let’s take a look why I created this function:
outputWeekDays.xhtml:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?xml version='1.0' encoding='iso-8859-1'?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" <t:dataList value="#{someBean.weekDaysAsArrayOfString}" var="weekDay" rowIndexVar="index"> #{index eq 0 ? '' : ', '} <h:outputFormat value="#{appLabel['week_days']}"> <f:param value="#{target:converterToInteger(weekDay)}"/> </h:outputFormat> </t:dataList> </html> |
In the example above I’m passing a week day number to week_days resource string used by outputFormat component.
This is the week_days resource string used in the code above:
1 2 | week_days={0,choice,1#sunday|2#monday|3#tuesday|4#wednesday| 5#thursday|6#friday|7#saturday} |
The resource string above is expecting a parameter, the appropriate week day name will be picked based on day number passed by param tag attached to outputFormat tag.
As we can see, I’m iterating over a array of strings, each element is a string and when I passed this string to a outputFormat component I got a conversion error, with this EL function I could solve my problem very fast.
Here we have two examples of how we can use EL function to solve some annoying problems, there’s a lot of jsf issues that can be solved in this way too!
Pretty good!!
Using Facelets the things become easier!
However, I didn\’t understand your last sample, in fact, I didn\’t understand the syntax on \
November 9th, 2008 | #
I am using facelet el function as said above. But whenever i am doing a form submission or action invokation null pointer exception is thrown. Could you please suggest solutions if any.
February 5th, 2009 | #
Which one are you talking about?
February 5th, 2009 | #
Hi,
I am talking about \’Simple loop over a range of numbers\’. I am using richfaces, a4j along with core jsf.
The exception is something like this :
java.lang.NullPointerException at org.apache.el.lang.FunctionMapperImpl$Function.writeExternal(FunctionMapperImpl.java:123) at java.io.ObjectOutputStream.writeExternalData(ObjectOutputStream.java:1310) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1288) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1079) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:302) at java.util.HashMap.writeObject(HashMap.java:1039) at sun.reflect.GeneratedMethodAccessor34.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:917) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1339) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1290) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1079) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:302) at org.apache.el.lang.FunctionMapperImpl.writeExternal(FunctionMapperImpl.java:74) at java.io.ObjectOutputStream.writeExternalData(ObjectOutputStream.java:1310)…….
February 6th, 2009 | #
Can you post the code and configuration at http://pastebin.com/ or send it to me by e-mail?
February 6th, 2009 | #
http://tinyurl.com/mcec3m
Facing new challenges everyday ยป Facelets EL Functions can save your day
February 15th, 2010 | #