Value-to-variable binding “let” tag for JSF, Facelets and Seam

The “let” tag enables you to bind the value of any expression to a variable and later reuse it, without recalculating the value. The concept comes of course from functional programming. It is especially useful with Seam’s extended EL.

The usage is really simple:

1
2
3
<mamut:let var="result" value="#{anyElExpression}">
   Now you can use #{result} as a normal variable.
</mamut>

I was looking at other tag libraries but couldn’t find anything similar. Or maybe there is?

The use case that motivated me to write this comes from the JBoss.ORG feeds application. There, in several places I have code similar to the following one:

1
2
3
4
5
6
7
8
9
10
<ui:repeat var="group" value="#{groupsService.all}">
   <s:fragment rendered=
      "#{groupsService.accFeeds(group).size() > 0}">
      (some header)
      <ui:repeat var="feed"
         value="#{groupsService.accFeeds(group)}">
         (...)
      </ui:repeat>
   </s:fragment>
<ui:repeat>

Of course calling groupsService.acceptedFeeds(group) twice is unnecessarily inefficient. I could move this call to a backing bean, but doing so would only cause me to write some really simple code many times. The version using the let tag calls the method only once:

1
2
3
4
5
6
7
8
9
10
11
<ui:repeat var="group" value="#{groupsService.all}">
   <mamut:let var="acceptedFeeds"
      value="#{groupsService.accFeeds(group)">
      <s:fragment rendered="#{accFeeds.size() > 0}">
         (some header)
         <ui:repeat var="feed" value="#{accFeeds}">
            (...)
         </ui:repeat>
      </s:fragment>
   </mamut:let>
<ui:repeat>

If somebody finds this useful, I’ve put the jar here. To use it, just bundle the jar with your application. The namespace for the tag is the following:

1
xmlns:mamut="http://mamut.net.pl/jsf"

Adam

  • Damian

    How is this different from c:set?

  • Hello,

    c:set is just an alias for an EL-expression. So, if you write:

    < c:set var="x" value="#{bean.method()}"/ >
    #{x} #{x}

    The method on the bean will be invoked twice. This is undesirable if the method call is expensive. Using the let-tag, it will be invoked once, and then the return value can be reused many times.

    Adam

  • Stephen

    Is there a technical reason that I have to use the tag’s body?
    That makes it kind of ugly to “let” more than one variable.

    In other words: Why not

    Now you can use #{result} as a normal variable.

  • Hello,

    no, I suppose you can as well write a bodyless tag
    (like < mamut:let var="x" value="#{exp}"/ > )
    and later just use #{x}.

    I just write it like this as it’s a direct translation from FP, where the let creates a scope where the variable is visible.

    Adam

  • Timothy

    I tried your let tag and it seems to be a great idea. However it seems to work intermittently and I don’t know enough about how it works to fix it.
    Sometimes it works fine, and sometimes the resulting variable evaluates as null (on the same page with no changes made to that page).

  • Hello,

    the sources are here:
    http://www.warski.org/let-tag-sources.tar.gz

    Maybe the value that you are trying to bind does resolve to null? Try to log (or System.out.println) the value in the method that is generating it, to see if it is evaluated and if so, what is the result.

    Adam

  • Gilad

    Hi Adam,

    I find your ‘let’ tag to be very useful dealing with JSF/Seam performance issues.
    May I use an adaptation of your code in my project (as source, not jar?)
    Thanks
    Gilad

  • Sure, I uploaded the source here:
    http://www.jboss.org/shotoku/downloads/other/
    (the source is really simple)

    The license is LGPL. Just please remember to retain the @author tags in comments.

    Adam

  • Eric Normandin

    Will this still properly bind a text field to the attribute on a backing bean?
    Eg I have a building.unit.address.xxx fields

    could I use
    and then on the address fields have #{address.xxx} ?

    Great work by the way.

  • No, this will capture the value and it won’t be updated later. So this is for read-only thing.

  • Apurba

    Thanks a lot for this component, looked at the source, you may want to add a saveState and restoreState to the UILet class, this will help in solving the NullPointerException that may come when the component is getting called in RestoreViewPhase. I also migrated LetTag from subclassing UIComponentTag to UIComponentELTag, please let me know if you would like the modified sources.

  • Sure, that would be great, maybe publish the sources somewhere, e.g. on GitHub, and link to them in a comment?

  • Pingback: seam performance tips()

  • rajendra

    How about setting that EL in pageContext

    like

    #{pageContext.set(‘x’,list.sublist)}
    #{x} and #{x}

    is there any problem in this? isnt it feasible?
    Please suggest me, I have a serious problem, in my case that list.sublist making few db calls.

    Thanks in advance.

  • Well, maybe this will work too, although I’m not sure about clearing the variable. You’d need to test :)

  • rajendra

    some formatting problem, in my case that list is a variable of a:repeat.

  • Aaron

    Hi Adam,

    I’d like to try out your solution, but unfortunately the link to the jar isn’t working. Could you maybe update it?

    Thanks,
    Aaron

  • Just remove the last component from the URL:
    http://www.jboss.org/shotoku/downloads

    Adam