Facelets EL Functions can save your day

Posted by – November 2, 2008

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
package br.com.druid.target.util;

import java.util.ArrayList;
import java.util.List;

public final class ELFunctions {

    private ELFunctions() {
       
    }
   
    public static List loopModel(Integer size) {
        List list = new ArrayList();
        for(int i = 0; i <= size; i++)
            list.add(i);
        return list;
    }
}

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
package br.com.druid.target.util;

public final class ELFunctions {

    private ELFunctions() {
       
    }
   
    public static int converterParaNumero(Object valor) {
        return Integer.valueOf(valor.toString());
    }
}

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!

Share

7 Comments on Facelets EL Functions can save your day

  1. Rafael Ponte says:

    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 \

  2. Jintu J says:

    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.

  3. rogerio says:

    Which one are you talking about?

  4. Jintu J says:

    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)…….

  5. rogerio says:

    Can you post the code and configuration at http://pastebin.com/ or send it to me by e-mail?

  6. http://tinyurl.com/mcec3m
    Facing new challenges everyday » Facelets EL Functions can save your day

  7. [...] always one to skirt convention I came across this blog post which summarizes nicely how you can create your own JSF 2 Expression Language functions which you [...]

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>