开发者

Calling a method with parameter inside JSTL loop

I have a JSP which needs to print some text which is produced by taking loop iterator and feeding it to anothe开发者_Go百科r object (Spring bean), something like:

<c:forEach var="myVar" items="${myVars}">
   <c:out value="anotherObject.getFoo(myVar)"/>
</c:forEach>

Obviously the code above isn't valid as JSTL . operator only allows parameter-less invocations. I can see following solutions to the problem:

1) Scriptlets

<c:forEach var="myVar" items="${myVars}">
  <%
    SomeType myVar = (SomeType) pageContext.getAttribute("myVar");
    SomeOtherType anotherObject = (SomeOtherType) pageContext.getAttribute("anotherObject");
    YetAnotherType result = anotherObject.getFoo(myVar);
    pageContext.setAttribute("result", result);
  %>
  <c:out value="${result}"/>
</c:forEach>

The obvious con here is JSP code pollution and general ugliness.

2) Writing a tag which does whatever is done inside scriptlets. Typical example of over-engineering, yuck!

3) Decompose a collection of myVars and replace each myVar with a dynamic proxy, InvocationHandler of which would add extra parameter-less method to make all getFoo() calls through anotherObject. All of that would be done in the controller so JSP would stay clean and myVar stays the same. But at what price?

I can not add .getFoo() method to the myVar because it doesn't fit there and would break the separation of concerns.

It looks like passing parameters will be possible in JSP/EL 2.2, but I'm using Tomcat 6.0.29 which only bundles EL 2.1 API.

Question: can anyone suggest the cleanest approach for this situation?


A simple Java-only "trick-fix" that works in the older JSTL version, too, and requires no extra taglibs/config/dependencies/frameworks, etc. is to "wrap" the function you want to call from JSTL in a class that extends from a Map class, and override its get() method.

As a minimal example, if you e.g. want to call the Math.sin() function from JSTL, you would define a class:

public class Sine extends HashMap<Double, Double> {
    private static final long serialVersionUID = 1L; // Avoids compiler-warning

    @Override
    public Double get(Object arg) {
        Double x = (Double) arg;
        return Math.sin(x);
    }
}

Then in your Action execute() method, you do:

...
request.setAttribute("sine", new Sine());
...

Then in jsp you can say:

  ${sine[0.75]}

to calculate the value Math.sin(0.75)

JSTL will treat the variable sine as a Map, but you can compute and return anything you like from the get() method.

I guess it gets a bit more involved if you have more than one argument to your function, but there should be workarounds for that, too :)


Here's how I've done it at the end.

Instead of passing a collection of SomeType instances, I am passing a map. Map keys are the same SomeTypes and values are instances of controller-specific inner class, let's call it SomeTypeSupplement.

SomeTypeSupplement adds necessary no-arg getters and wires everything together. JSP now iterates over map entries and is able to retrieve the data via JSTL.

This way I avoid Proxy magic, unnecessary TLDs, keep JSPs tidy and reasonably type-safe.


If you can't abide with scriptlets (your alternative 1), I would create a custom tag for it (your alternative 2) or a custom EL function. I don't agree that it's "over-engineering", it's using the available tools for their intended purpose.

Your alternative 3, though, I don't like. If anything, THAT'S over-engineering plus it will make your system unnecessarily complex and harder for other people to follow. Adhere to the intentions of the specifications and standards you are working within. Don't make things harder or more complex for the sake of it.


Why not just compose your object list in your backend Java code and then just use JSP to display it?


Wanted to add a comment to the last (by time) answer of Rop, but missing "reputation", so here I am with an answer.

I shortly had the same idea with such a map, but tried to make it more generally usable, having a "DynamicMap", DynamicMapCalculator interface and letting you wrap any method call into such a map (without the need to make a new map implementation every time, just using anonymous class instantiation).

This would be the topic, if you are interested: A qu. of style: dynamic map JSTL hack to work around missing parameter function calls

And I would be interested in opinions there: is this something you may do without a bad conscience?


Another option is to use Velocity. It is much nicer than jstl.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜