Thymeleaf - replace c:url and c:param
October 03, 2017
As part of my conversion from jsp’s to Thymeleaf I came across a block that was creating url’s using c:url and c:param for the url parameters.
So there is several things going on in the code
<fmt:message key="linkDesc" var="linkDescVar"/>
<c:set value="?${pageContext.request.queryString}" var="queryString"/>
<c:set var="baseUrl" value="/test/app"/>
<c:if test="${queryString == '?'}">
<c:set value="" var="queryString"/>
</c:if>
<c:set var="testLink" value="${pageContext.request.requestURI}${queryString}"/>
<c:url value="${baseUrl}" context="/" var="baseUrlLink">
<c:param name="link" value="${testLink}"></c:param>
<c:param name="linkDesc" value="${linkDescVar}"></c:param>
</c:url>So it’s forming a url link to test/app and adding parameters of where it’s currently at including the current query string. So if you go to say http://localhost:8080/pub/home?key=value it should form a link that would go to http://localhost:8080/test/app?link=home?key=value&linkDesc=testit
The linkDesc comes from the messages.properties file
So things are done this way because of how the jsp taglibs work. The Thymeleaf solution might not be a line by line replacement. But lets go through it line by line, look at the options in Thymeleaf then see about mushing it all together into something that works.
<fmt:message key="linkDesc" var="linkDescVar"/>
The normal replacement for fmt:message would be th:text but in this case it’s using it to get a value from the properties file and store it in a variable. Don’t think that’s a option for th:text
So a direct replacement I think would be
<div th:with="linkDescVar=#{linkDesc}"></div>Which sets up the linkDescVar within the context of the div (reckon the within the div bit is something that’s worth noting as the fmt:message var is available to the whole page)
Another variant would be
<div th:with="linkDescVar=${#messages.msgOrNull('linkDesc')}"></div>This uses the #messages utility so can be used directly in expressions, might be useful later as there may not be a need to create the variable anymore.
<c:set value="?${pageContext.request.queryString}" var="queryString"/>
The replacement for c:set I believe is th:with like above. The request object is available in Thymeleaf as request so this can be a fairly straight replacement
<div th:with="queryString=${'?' + #request.queryString}"></div>But there’s some gotcha’s here, while c:set returned blank if ${pageContext.request.queryString} was blank Thymeleaf returns null so in the case where there are no query parameters you get ?null which is not what we want.
The code here
<c:if test="${queryString == '?'}">
<c:set value="" var="queryString"/>
</c:if>Handled the case of no query parameters by checking for just the ? and replacing it with blank.
This needs to be handled in the th:with in Thymeleaf I believe so this
<div th:with="queryString=${#request.queryString == null ? '' : '?' + #request.queryString}"></div>an awful lot of question marks but this is using (if) ? (then) : (else) in the expression. If it’s null set queryString to blank else set it you a ? and concat on the query string.
<c:set var="testLink" value="${pageContext.request.requestURI}${queryString}"/>
Our friend th:with again for this job, which is just concatenating two values.
<div th:with="testLink=${#request.requestURI + queryString}"></div>Last part
<c:url value="${baseUrl}" context="/" var="baseUrlLink">
<c:param name="link" value="${testLink}"></c:param>
<c:param name="linkDesc" value="${linkDescVar}"></c:param>
</c:url>Yea th:with again, the go to variable assignment wizard.
So some Thymeleaf url magic beans to cover first, for forming url’s use @{...} which handles alot of the url dark arts, context root etc… within that to add parameters you use () so @{/test/app(key=value)} to get the context to be server root like context="/" you use a tilde ~ at the start of the url.
With that in mind
<div th:with="baseUrlLink=@{~/test/app(backLink=${testLink},linkDesc=${linkDescVar})}"></div>Creates the link and assigns it to the baseUrlLink variable and uses the two other variables set up earlier testLink and linkDescVar
This gives an overall block like this
<div th:with="linkDescVar=#{linkDesc}">
<div th:with="queryString=${#request.queryString == null ? '' : '?' + #request.queryString}">
<div th:with="testLink=${#request.requestURI + queryString}">
<div th:with="baseUrlLink=@{/test/app(backLink=${testLink},linkDesc=${linkDescVar})}">
<a th:href="${baseUrlLink}">example of link</a>
</div>
</div>
</div>
</div>So seeing as these are all expressions you can collapse them down as much as you like for example you could move the linkDescVar inline.
<div th:with="queryString=${#request.queryString == null ? '' : '?' + #request.queryString}">
<div th:with="testLink=${#request.requestURI + queryString}">
<div th:with="baseUrlLink=@{/test/app(backLink=${testLink},linkDesc=${#messages.msgOrNull('linkDesc')})}">
<a th:href="${baseUrlLink}">example of link</a>
</div>
</div>
</div>In theory you can get the whole thing on one line if you wanted, that would be a complex looking line but you might prefer it that way.
- ← IntelliJ clean up JPA generated models for use with Lombok
- Sonarqube on Docker for Mac configuring a proxy →