You see, JSPs are essentially hybrids. They are a way for coders to create HTML and mix in some Java.
Now, way back when JSPs were first created, you could embed native Java code right onto your JSP (read: onto your presentation layer).
This has quickly been labelled as a horrendous programming practice that should be avoided at all costs.
Fair enough…
But we still continued to use JSPs and instead used tag libraries to add additional markup to our JSP code.
For example, we would use something like the “C” JSTL (a tag library for JSPs) in order to introduce some control structures and additional functionality.
This was a much better approach than using plain old Java code embedded right onto JSPs, but it still wasn't the greatest solution.
The reason why this wasn't a great solution was because we are still using non-native HTML code inside of our HTML pages (well… We call them JSPs, but they're essentially just HTML pages).
Remember that the convention is to separate your concerns as best as possible… Which is why we tend to put our CSS and JS code in external files and link them up inside our HTML / JSPs.
So why is it that we're allowed to use glorified Java code in our JSPs, but not anything else?
The Main Issue with JSPs
Okay fine, if we drop the argument that we should separate our concerns, then why else are JSPs a bit crappy?
Well, they can't really be opened outside of the web server context.
What happens if you shut down your web server, then try to access a JSP?
Or what happens if you just try to open a plain old JSP inside of your browser?
I bet you it's not going to look like it should.
That's because JSPs rely on the web server to render properly.
So how can we write native HTML code, but still be able to have our HTML pages properly interact with our back-end server?
Enter Thymeleaf
First thing's first. Let's all get on the same page with how to pronounce Thymeleaf.
It's “Time – Leef”. Thyme is pronounced like the herb, which is pronounced “Time”, like the time of day.
Awesome, now that SUPER important detail is out of the way…
Thymeleaf solves all the problems that have plagued JSPs since the beginning of time.
With Thymeleaf you can write native HTML code that any old browser would be able to render, and yet still be able to interact as you always could, with your Java server.
Boo yah!
And, thankfully, it's not too tough to learn.
I plan on teaching you most of what you need to know in this one post.
Getting Started with Thymeleaf
This can either be ridiculously simple… Or it can be simple.
Either way, getting up and running with Thymeleaf is easier than putting on my socks in the morning.
Are you using Spring Boot?
If you're using Spring Boot, then you're already good to go. You literally don't have to do anything to start using Thymeleaf.
Ridiculously simple right?
Okay, but what if you aren't using Spring Boot.
Well then it's as simple as either putting in an artifact into your dependency management system (i.e. adding a dependency into your Maven pom.xml
file) or just manually downloading the JAR and adding it to your classpath.
There are some really simple installation instructions via the Thymeleaf download page.
So, for example, if you're not using Spring Boot but you're still working with a Spring based project, then you'd add the core library plus the appropriate Spring version integration. Let's assume you're working with Spring 4 without Spring Boot, here's what you would add to your maven pom.xml
file:
org.thymeleaf thymeleaf 3.0.0.RELEASE org.thymeleaf thymeleaf-spring4 3.0.0.RELEASE
Thymeleaf Syntax
Alright, so you've installed Thymeleaf, now what?
Well, it's time to start writing some code.
For my code, I'm going to assume we're working within the confines of our Food Diary application that we've been building over the past few weeks.
Note: You can check out the GitHub repository for this project here.
Simple IF Statements in Thymeleaf
We'll base this on whether or not there is a list of FoodDiary
objects on our model.
<html xmlns:th="http://thymeleaf.org"> ...<html>
The first thing I'll mention (and this is the only time I'll mention it): you'll need to define the “th” xml namespace by adding xmlns:th="http://thymeleaf.org"
to the <html>
tag. When you do this, you won't get any warnings in your IDE saying that the thymeleaf attributes are not found.
Okay having said that, let's continue… Please note that in the code above, it isn't the most efficient way of trying to iterate through collections, as the check to see if it's not null can be done when iterating.
How to Iterate Over Collections with Thymeleaf
This one's got a slightly strange syntax, most of the code you'll write with Thymeleaf will be wrapped entirely within the Spring Expression Language syntax (SPEL for short)… this syntax is the dollar sign followed by curly brackets “%{}”.
But when performing a for each
type loop with Thymeleaf it's just a bit different. Here's how we would iterate through our foodDiaries
:
Displaying Information from Model with Thymeleaf
Next up is something we do all the time… display the information we have on our model.
In Thymeleaf there are two main ways of accomplishing this task… the first is with th:text
like so:
The use of th:text
plus String concatenation will result in a String like this: “I had pizza for dinner at 6:00pm”.
The th:text
attribute can be used within many tags too, not just a div
. You can use it in span
tags, header tags, li
tags… you name it.
That's the beauty of Thymeleaf, a lot of their attributes can be used with many tags.
Now, constantly writing th:text=""
inside of span
and/or div
tags would get a bit tiresome if there was a lot of outputting that needed to be done.
So Thymeleaf addresses this by allowing in-lining of outputs like so:
I had [[${aFoodDiaryObj.food}]] for [[${aFoodDiaryObj.mealType}]] at [[${aFoodDiaryObj.timeOfDay}]]
Linking HTML Elements to Model Attributes with Thymeleaf
Alright, so we're moving right along with this Thymeleaf syntax. Next up is something else that's critical to building web applications… how to link your input elements to your Spring Controllers so that you can transfer data from HTML to Java.
Let's assume that we want to populate a FoodDiary
object in HTML and have it link to our Java back-end.
Previously, when using JSPs we would make use of the form
JSTL and we would create <form:input>
elements.
Well, once again, Thymeleaf is supposed to be able to be interpreted by a browser without the use of a running web server. So a browser doesn't know what the heck a <form:input>
element is without something like a Tomcat server.
Thymeleaf just uses its th:field
syntax to link everything up.
Note: Let's assume that we have a foodDiary
item that's been added to our model from a controller.
As you can see, we're using the name of the variable that's been added to our model (foodDiary
) inside of our reference to th:field
in order to link everything up with our controller.
Now when we submit this form (assuming our controller is properly hooked up), the Java method that's called will be able to translate this HTML form data into an actual FoodDiary
Java object.
Mission accomplished!
But, we're not done yet… there's a small shortcut that you can use to make your life just a little bit easier.
You can make use of a th:object
attribute to sort of “store” a reference to a model attribute when you're using the th:field
code.
This is merely a way to reduce the amount of keystrokes you need to make… so there's literally no difference in functionality here… it's all about sheer laziness!
This code would work in exactly the same way as the code above:
So the two things to note above are:
- We used the
th:object
attribute on the form to “store” the reference to the model attribute - We used the *{} syntax as opposed to the ${} and we omitted the reference to the model attribute to save keystrokes
Again, all the code above does is saves you from having to type ${foodDiary.someProperty}
and instead replaces it with *{someProperty}
.
Utility Objects in Thymeleaf
From time to time we've all needed to go searching for the tag library that will help us format a date / currency or some other random utility based task that the standard “C” or “FORM” tag libraries couldn't help us with.
Well, thankfully the good folks at Thymeleaf thought of this and they provide many expression utility objects.
Here's a quick example of how you could format a date:
Today is:
Or how about checking to see if a list is not empty?
How to use Thymeleaf as it was Intended to be Used…
Now, if you're an established Thymeleaf user, and you've been using it properly… you might be a little upset with my tutorial thus far. Rightfully so, I've left out one of the key reasons for which Thymeleaf was created.
You see, Thymeleaf was created so that you could open up an HTML file with a whole bunch of Thymeleaf markup in it, but the page would still open up and look fairly normal. Meaning, you don't have to view a Thymeleaf enabled HTML page with a server running.
But if you don't have a server running, and Thymeleaf is a server based library, then how will your HTML page look right?
Well, through clever use of default values.
You see, whenever you create a Thymeleaf enabled HTML element, you should also provide some sort of default value so that if you were to open the HTML page without a server running, it could fall back to those defaults.
Let me show you an example:
Welcome to my Website
I ate cookies
Do you notice how we hard-coded some text in the code above?
We put in “Welcome to my Website” in the h1
tag and we also put “I at cookies” in the span
tag.
And what you'll notice is that if you were to open that up in a browser without a server running, you would see exactly those two sentences displayed on the screen… because the browsers will just ignore the thymeleaf markup completely. And if you ignore the Thymeleaf markup, then you're left with a plain old page that welcomes you and tell you that it ate cookies.
However, if you open this page while a web server is running with Thymeleaf enabled, Thymeleaf will recognize its own markup and it will replace the “default” values you supplied with the expected data that exists on the model.
Pretty neat stuff.
You can even use Thymeleaf in JavaScript as well.
Using Thymeleaf in JavaScript
Let's assume you'd like to get access to some values that are on the model, but you're inside of a JavaScript code snippet.
No problemo!
You just need to use the th:inline="javascript"
attribute along with some CDATA markup to make everything work.
It's a bit convoluted, but hey, it works!
Here's how I would get the value of a property from the model assuming there's a foodDiary
object available to work with:
So there are three main things you need to take note of here:
- Make sure you use the
th:inline="javascript"
code in yourscript
tag. - Don't forget to include the opening and closing CDATA markup inside your
script
tag. - It's recommended to comment out the inlined code in your JavaScript so that Thymeleaf can make use of default values.
In the end, you can get away with NOT commenting out your inlined Thymeleaf code, but then you wouldn't really be using good practices with your Thymeleaf code.
So for the sake of completion, here's what the above code would look like if you weren't using proper Thymeleaf coding principles:
Again, both of the code snippets above accomplish the same overall task, the only difference is that the second code snippet won't work as well when you're loading the page outside of a running web server.
Contructing Dynamic URLs with Thymeleaf
At some point in your web application coding journey, you will likely run into the situation where you'll need to generate a dynamic URL.
Meaning, you'll need to create a URL that contains information that will vary depending on the situation.
A very common scenario is one where you want to click on a link to see more information about a specific row in your database.
So for our specific scenario, let's say we want to have a page that lists out links to all of our FoodDiary
items.
Alright, so this syntax is also a bit strange, but you can figure it out with some of my expert teaching *wink wink, nudge nudge*.
First thing to note is that we use the “at” syntax for URLs: @{...}
. This is a special syntax just for URLs because they are THAT important.
Next thing you'll see is that we've introduced a th:href
syntax for Thymeleaf URLs. The th:href
attribute is what enabled the use of the @{...}
notation.
Note: if you are an astute coder, you might be asking yourself a question: “If I can use th:href
does that mean I can throw th:
in front of any existing html attribute? The answer is… pretty much! You can use th:id
to assign a dynamic ID with the Thymeleaf + SpEL syntax, th:action
inside of a <form>
tag. In fact, you can see the entire list of these values here.
I'll mention here in passing that we have both an href
and a th:href
for the sake of Thymeleaf best practices… again, remember that the browser will ignore th:href
when loaded outside of a web server, so if th:href
is ignored, we have an href
to fall back on. When this is run inside a web server context, the href
will be ignored in favor of the th:href
.
Hopefully everything makes sense so far, but now let's get into the content within that special “at” syntax.
Inside our th:href
“at” syntax we have put in the following: /food-diaries/{foodDiaryId}(foodDiaryId=${foodDiary.id})
This combines a plain old URL with the concept of a path variable (like in Spring). The difference is that we need to define our path variables right inside our th:href
attribute (there aren't any annotations like in Spring).
The syntax is fairly straight-forward, first you define a path variable by putting it in your URL with curly brackets (i.e. {foodDiaryId}
).
Note: you can add more than just one path variable in a URL, you'd just continue declaring path variables just like we did with {foodDiaryId}
in the URL. You won't assign values to the path variables until the end of the URL.
Once you're done with declaring path variables, then you'll need to assign values to them. This is accomplished by adding round brackets with an assignment statement: (foodDiaryId=${foodDiary.id})
.
We say foodDiaryId=
because that's the name we used when we declared our path variable. Then we finish with ${foodDiary.id}
using SpEL because that's how we can pull an actual value from the list of FoodDiary
items that we're currently iterating over.
Done and done.
Using Multiple Path Variables in a Single Dynamic URL with Thymeleaf
Just for the sake of completion again, here's a quick example of what it would look like to have more than one path variable defined in one URL:
The main thing to note here is that we don't actually start assigning values to our path variables until the end of the URL… and the assignment syntax is comma separated.
Cool?
Cool.
Getting Iteration Details When Performing For Each Loop in Thymeleaf
Switching gears now, let's close things off by talking about more advanced iteration techniques.
Sometimes it's useful to have more information about the stuff you're iterating over.
This could previously be accomplished in JSPs using the <c:forEach varStatus="">
syntax… and thankfully, Thymeleaf has a way to get access to that exact same information.
Bear with me again, the syntax is a bit different from the “c” tag library, but it's simple. Let's iterate over our FoodDiary
items once more, but I want to get access to the iteration information:
The code above will print out something like this:
0 even
1 odd
2 even
3 odd
4 even
5 odd
So, the way to get access to the iteration information is just to add a comma after you declare the variable name for the iteration, and give your iteration status variable a name. I just happened to call it iterStatus
, but you can call it whatever you like, it's just a variable name.
Then once I've declared my iteration status variable, I can access it within the scope of my loop (in the case the scope is the div
tag.
There are some other properties that the iteration status variable holds. You can get the full list from the good folks at thymeleaf.