Creating a multi-column layout in a JSP using JSTL can be a difficult concept to master. Over at my day job, we’ll see this concept occasionally. We’ll have a block of data that we’d like to present in a series of columns, with a maximum number of items in each column. Here is a quick tutorial to outline a solution to this pattern. We’ll be using Sun’s Core JSTL tag library, to do the heavy lifting, along with CSS and HTML.
The Setup
Let’s assume we’ll have at most 30 items in our set, and we’ll want a maximum of 10 items in each column, giving us a three-column layout. For this example, we’ll also put our items in an ordered list so that we can see what item number we’re working with. Let’s set that up in code.
-
CSS — You’ll need a simple column layout that accounts for the fact that you may have any number of columns, depending on how much data you’ll be working with. I’ll use floats to make it simple, and I’ll add a border to each successive column for a little visual flair. My CSS would look like this:
div.leftCol{
float:left;
width:200px;
}
div.middleCol, div.rightCol{
border-left:1px solid #CCC;
float:left;
padding-left:20px;
width:200px;
}
-
HTML — This one is up in the air. You may have three divs, you may have one. But, they’ll all look something like this:
<div class="leftCol">[[10 items]]</div>
We know what we’ll be working with, but we’ll need some creative use of JSTL to actually get these elements to fall into place and enforce our maximum values per column rule.
The JSTL
We’ll be making use of two JSTL Core constructs: the <c:if> tag and the <c:forEach> loop.
- <c:if> works like any standard if test. We’ll use it to test column data boundaries.
- <c:forEach> gives us a hook called "varStatus" that lets us peek into the status of the iteration. We’ll be looking specifically at the index property of that varStatus object.
Writing the Code
Let’s start with a skeleton JSTL loop, so that we have a little scaffold to build on.
<c:forEach items="${someItems}" var="item" varStatus="loop">
<li>${item}</li>
</c:forEach>
That’s a start. We still have to figure out where to place our starting and closing div tags and our starting and closing ol tags, which is the problem concept behind this pattern. Luckily, there’s a clever way of solving this problem.
Assume that we know that there will be at least one item to work with. (If there’s a chance for zero, use a <c:choose> block to react appropriately, dealing with the zero condition separately, and using this method as-is.) With at least one item, we’ll always need a left column and a start <ol> tag, no matter what. We’ll always need closing tags for our left column div and an ol, too. Knowing that these are always required, we’ll move them outside the loop, so that they’re always displayed.
<div class="leftCol">
<ol>
<c:forEach items="${someItems}" var="item" varStatus="loop">
<li>${item}</li>
</c:forEach>
</ol>
</div>
We now have at least one column and list. We can use simple if tests to figure out when to close that first column and list and begin a second. In pseudocode, if we are at our maximum number of items, print a
close ol tag, print a close div tag, print a new open div tag with appropriate class, and print a new open ol tag. So, if we’re at a maximum number of items (in our case, 10 and 20), we’ll close that column and start a new one. Let’s add that concept to our block of code, and finish this pattern. (Remember that loop.index gives us a count of which item we’re on.)
<div class="leftCol">
<ol>
<c:forEach items="${someItems}" var="item" varStatus="loop">
<li>${item}</li>
&;lt;c:if test="${loop.index == 10}">
&;lt;c:if>
&;lt;c:if test=”${loop.index == 20}”>
&;lt;c:if>
</c:forEach>
</ol>
</div>
That above code may take a few seconds to sink in. The trick is that the </ol> and </div> tags after the loop will close any <ol> and <div> tags that are open at that point. It could be the left column that those tags close, or the middle column, or the right column. Let’s think about that for a second.
- If there are 4 items in the list, we print the opening tags, enter the loop, print 4 items, exit the loop, print the close tags.
- If there are 17 items in the list, we print the opening tags, enter the loop, print 10 items, print the close tags for the left column, print the opening tags for the middle column, print 7 items, exit the loop, print the close tags.
- If there are 24 items in the list, we print the opening tags, enter the loop, print 10 items, print the close tags for the left column, print the opening tags for the middle column, print 10 items, print the close tags for the middle column, print the opening tags for the right column, print 4 items, exit the loop, print the close tags.
As you can see, we’re using the nature of HTML (a closing tag closes the nearest opening tag, regardless of any attributes that opening tag may have) to simplify our work and save many lines of JSTL. In the meantime, we have a simple pattern that will allow us to create as many columns as we want (simply create a generic CSS column class) with as many items in each as we want (simply change the boundary values in the JSTL). The key to the puzzle: keeping code we know we’ll always need outside of the loop so that it will always be printed and taking advantage of the nature of HTML.
technorati tags:css, jsp, jstl, howto, layouts