Skip to content

Fun with JSTL in JSPs

David Ensinger

It’s often difficult to find good documentation for JSTL. It’s either too technical or is so poorly designed that you swear you’re browsing within an enterprise Java intranet (is anyone surprised?). Since I’ve often found myself searching for the same information over and again, I’ve taken the time to write down the features I use most frequently.

Comments

To comment out code, it’s preferable to use hidden comments, which don’t get printed:

<%-- This will never been seen on production. --%>
<!-- But this will, although why would you want that? -->

Operators

Both arithmetic and logical operators are supported:

Table of Operators:
OperatorDescription
.Access a bean property or Map entry
[]Access an array or List element
( )Group a subexpression to change the evaluation order
+Addition
-Subtraction or negation of a value
*Multiplication
/ or divDivision
% or modModulo (remainder)
== or eqTest for equality
!= or neTest for inequality
< or ltTest for less than
> or gtTest for greater than
<= or leTest for less than or equal
>= or gtTest for greater than or equal
&& or andTest for logical AND
|| or orTest for logical OR
! or notUnary Boolean complement
emptyTest for empty variable values

Set

Use the set tag to create a new variable or assign a value to a new or existing variable.

Table of Attributes:
AttributeDescriptionRequiredDefault
valueInformation to saveNobody
targetName of the variable whose property should be modifiedNoNone
propertyProperty to modifyNoNone
varName of the variable to store informationNoNone
scopeScope of variable to store informationNoPage

An Example

The following two variables have their respective values assigned with set:

<c:set var="maxTopics" value="3" />
<c:set var="displayClass" value="hiddenTablet" />

We’d then be able to use the variables like so, ${maxTopics} and ${displayClass} within our JSP.

If

To evaluate an expression, use an if tag. If the condition is true, it will display the body content.

Table of Attributes:
AttributeDescriptionRequiredDefault
testCondition to evaluateYesNone
varName of the variable to store the condition's resultNoNone
scopeScope of the variable to store the condition's resultNopage

An Example

In the following example of an author profile component, we test if the author has a photo and if so, we print it out:

<div class="c c-authorProfile">
  <h4 class="authorProfile-heading">${authorProfile.fullName}</h4>
  <c:if test="${authorHasPhoto}">
    <img src="${authorProfile.photo}" alt="${authorProfile.fullName}" class="authorProfile-photo" />
  </c:if>
  <div class="authorProfile-biography">
    ${authorProfile.biography}
  </div>
</div>

Choose

Much like a switch of if else statement, choose allows you to evaluate multiple conditions by using a combination of when and otherwise tags.

An Example

This media object component tests if there’s a photo, or a video, or neither and then prints out the appropriate markup:

<div class="c c-mediaObject">
  <h4 class="mediaObject-heading">${mediaObject.heading}</h4>
  <c:choose>
    <c:when test="${mediaObject.hasPhoto}">
      <img src="${mediaObject.photo}" alt="${mediaObject.description}" class="mediaObject-photo" />
    </c:when>
    <c:when test="${mediaObject.hasVideo}">
      <video controls class="mediaObject-video">
        <source src="${mediaObject.video}" type="video/mp4" />
      </video>
    </c:when>
    <c:otherwise>
      <p class="author-warning">Hey did you forget to author a photo or video? If not, perhaps a different component would be more appropriate.</p>
    </c:otherwise>
    <div class="mediaObject-text">
      ${mediaObject.text}
    </div>
  </c:choose>
</div>

Ternary Operations

A ternary operator, which allows for an inline condition that evaluates to true or false:

${condition ? 'when_true' : 'when_false'}

A Couple Examples

Using a ternary operator is useful within a template because it allows for the addition of classes for use as styling hooks.

Here we use a ternary operator to print classes that denote whether the layout is one or two columns:

<body class="t t-${templateName} ${hasTwoColumnsLayout ? 'l-twoColumns' : 'l-oneColumn'}"></body>

It may also be helpful to print out inline styles, although be weary about relying on this too often, as CSS should really be kept in stylesheets.

In this example we change the background color of the component to red when it’s not authored:

<div class="c c-${componentName}" style="${componentNeedsAuthoring ? 'background-color: red' : ''}"></div>

For Each

To iterate over a collection, use a forEach tag. There are a half-dozen attributes for use with the forEach tag and they can provide quite a bit of flexibility.

Table of Attributes:
AttributeDescriptionRequiredDefault
itemsInformation to loop overNoNone
beginElement to start with (0 = first item, 1 = second item, …)No0
endElement to end with (0 = first item, 1 = second item, …)NoLast element
stepProcess every step itemsNo1
varName of the variable to expose the current itemNoNone
varStatusName of the variable to expose the loop statusNoNone

An Example

Here we have a component that displays an unordered list of links:

<div class="c c-linkList">
  <h4 class="linkList-heading">${linkList.heading}</h4>
  <ul class="linkList-list">
    <c:forEach var="link" items="${linkList.links}">
      <li class="linkList-item"><a href="${link.href}" class="linkList-link">${link.text}</a></li>
    </c:forEach>
  </ul>
</div>

forEach varStatus Properties

The varStatus attribute comes with some helpful properties.

Table of varStatus Properties:
PropertyGetterDescription
currentgetCurrent()The item (from the collection) for the current round of iteration
indexgetIndex()The zero-based index for the current round of iteration
countgetCount()The one-based count for the current round of iteration
firstisFirst()Flag indicating whether the current round is the first pass through the iteration
lastisLast()Flag indicating whether the current round is the last pass through the iteration
begingetBegin()The value of the begin attribute
endgetEnd()The value of the end attribute
stepgetStep()The value of the step attribute

An Example

A couple useful properties are first and last, which are used to delimit a list of authors in the following example from a byline component:

<div class="c c-authorByline">
  <c:forEach var="author" items="${site.authors}" varStatus="status">
    <span class="author-byline">
      <c:if test="${status.first and not status.last}">by</c:if>
      <c:if test="${not status.first and status.last}">and</c:if>
      <a href="${author.linkHref}" class="author-byline--link">${author.fullName}</a>
      <c:if test="${not status.first and not status.last}">,</c:if>
    </span>
  </c:forEach>
</div>

Functions

There are a lot of standard functions included in JSTL, although you probably shouldn’t use them. The majority of the functionality they provide is either better done in the model (and not the view) or can be accomplished with CSS. I very seldom find myself using anything other fn:length(), which I use to find the number of items in a collection.

Table of Functions:
FunctionDescription
fn:contains()Tests if an input string contains the specified substring.
fn:containsIgnoreCase()Tests if an input string contains the specified substring in a case insensitive way.
fn:endsWith()Tests if an input string ends with the specified suffix.
fn:escapeXml()Escapes characters that could be interpreted as XML markup.
fn:indexOf()Returns the index withing a string of the first occurrence of a specified substring.
fn:join()Joins all elements of an array into a string.
fn:length()Returns the number of items in a collection, or the number of characters in a string.
fn:replace()Returns a string resulting from replacing in an input string all occurrences with a given string.
fn:split()Splits a string into an array of substrings.
fn:startsWith()Tests if an input string starts with the specified prefix.
fn:substring()Returns a subset of a string.
fn:substringAfter()Returns a subset of a string following a specific substring.
fn:substringBefore()Returns a subset of a string before a specific substring.
fn:toLowerCase()Converts all of the characters of a string to lower case.
fn:toUpperCase()Converts all of the characters of a string to upper case.
fn:trim()Removes white spaces from both ends of a string.

An Example

In the following example we only print out the search results if there are more than zero, otherwise we tell the user to try a different search term.

<div class="c c-siteSearch">
  <h4 class="siteSearch-heading">${siteSearch.heading}</h4>
  <c:choose>
    <c:when test="${fn:length(siteSearch.queryResults) > 0}"></c:when>
    <c:otherwise>
      <p class="siteSearch-warning">Sorry your search has found no results. Please try a different query!</p>
    </c:otherwise>
  </c:choose>
</div>