I've been told that the use of scriptlets (<%= ... %>) in my JSP pages isn't such a great idea.
Can someone with a bit more java/jsp experience please give me some pointers as to how to change this code so its more 'best practice', whatever that may be?
This JSP is actually my sitemesh main decorator page. Basically my web design has a tab strip and a submenu, and i wish to somehow highlight the current tab and show the correct submenu by looking at the current request URI.
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %> <html> <head> <title>My Events - <decorator:title /></title> <link href="<%= request.getContextPath() %>/assets/styles.css" rel="stylesheet" type="text/css" /> </head> <body> <div class="tabs"> <a <%= request.getRequestURI().contains("/events/") ? "class='selected'" : "" %> href='<%= request.getContextPath() %>/events/Listing.action'>Events</a> <a <%= request.getRequestURI().contains("/people/") ? "class='selected'" : "" %> href='<%= request.getContextPath() %>/people/Listing.action'>People</a> </div> <div class="submenu"> <% if(request.getRequestURI().contains("/events/")) { %> <a href="Listing.action">List of Events</a> |<a href="New.action">New Event</a> <% } %> <% if(request.getRequestURI().contains("/people/")) { %> <a href="Listing.action">List of People</a> |<a href="New.action">New Person</a> <% } %> </div> <div class="body"> <decorator:body /> </div> </body> </html>
Thanks all
Its not a clean design to mingle code with view logic. This is why JSP is not ideal solution. You should use templates like Velocity/Freemarker instead which does not allow mixing java code at all.
When the scripting language is set to java, a scriptlet is transformed into a Java programming language statement fragment and is inserted into the service method of the JSP page's servlet. A programming language variable created within a scriptlet is accessible from anywhere within the JSP page.
There are three main subdivisions of scripting elements in JSP. They all provide different ways to include java code in a JSP file.
In JSP, java code can be written inside the jsp page using the scriptlet tag.
I think it helps more if you see with your own eyes that it can actually be done entirely without scriptlets.
Here's a 1 on 1 rewrite with help of among others JSTL (just drop jstl-1.2.jar
in /WEB-INF/lib
) core and functions taglib:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> <html> <head> <title>My Events - <decorator:title /></title> <link href="${pageContext.request.contextPath}/assets/styles.css" rel="stylesheet" type="text/css" /> </head> <body> <div class="tabs"> <a ${fn:contains(pageContext.request.requestURI, '/events/') ? 'class="selected"' : ''} href="${pageContext.request.contextPath}/events/Listing.action">Events</a> <a ${fn:contains(pageContext.request.requestURI, '/people/') ? 'class="selected"' : ''} href="${pageContext.request.contextPath}/people/Listing.action">People</a> </div> <div class="submenu"> <c:if test="${fn:contains(pageContext.request.requestURI, '/events/')}"> <a href="Listing.action">List of Events</a> |<a href="New.action">New Event</a> </c:if> <c:if test="${fn:contains(pageContext.request.requestURI, '/people/')}"> <a href="Listing.action">List of People</a> |<a href="New.action">New Person</a> </c:if> </div>
Here's a more optimized rewrite, note that I used c:set
to "cache" expression results for reuse and that I use HTML <base>
tag to avoid putting the context path in every link (just make all relative URL's in your webpage relative to it --without the leading slash!):
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> <c:set var="isEvents" value="${fn:contains(pageContext.request.requestURI, '/events/')}" /> <c:set var="isPeople" value="${fn:contains(pageContext.request.requestURI, '/people/')}" /> <html> <head> <title>My Events - <decorator:title /></title> <base href="${pageContext.request.contextPath}"> <link href="assets/styles.css" rel="stylesheet" type="text/css" /> </head> <body> <div class="tabs"> <a ${isEvents ? 'class="selected"' : ''} href="events/Listing.action">Events</a> <a ${isPeople ? 'class="selected"' : ''} href="people/Listing.action">People</a> </div> <div class="submenu"> <c:if test="${isEvents}"> <a href="Listing.action">List of Events</a>|<a href="New.action">New Event</a> </c:if> <c:if test="${isPeople}"> <a href="Listing.action">List of People</a>|<a href="New.action">New Person</a> </c:if> </div>
It can actually be optimized more if you collect all those "hardcoded" values like events
and people
and link texts in a Map
in the application scope and use under each the JSTL <c:forEach>
to display the tabs.
As to your actual question, you can disable scriptlets (and get runtime errors about using it) by adding the following entry in webapp's web.xml
. It may help to spot overseen scriptlets.
<jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <scripting-invalid>true</scripting-invalid> </jsp-property-group> </jsp-config>
To learn more about EL, check the Java EE tutorial part II chapter 5. Implicit EL objects, such as ${pageContext}
are described here. To learn more about JSTL, check the Java EE tutorial part II chapter 7. Note that JSTL and EL are two separate things. JSTL is a standard taglib and EL just enables to access backend data programmatically. Although it is normally used in taglibs like JSTL, it can also be used standalone in template text.
As an aside, is
<%= request.getContextPath() %>
an acceptable use of scriptlets that isn't frowned on so much?
This may be an unpopular opinion, but if all you do are simple conditionals and text insertions, I cannot find much fault in the use of scriptlets. (Note the if)
I'd probably use JSTL and the expression language, but mostly because it can be less typing, and IDE support may be better (but a good JSP IDE can also find missing closing brackets and stuff like that).
But fundamentally (as in "keep logic out of templates") I fail to see any difference between
<% if(request.getRequestURI().contains("/events/")) { %>
and
${fn:contains(pageContext.request.requestURI, '/events/')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With