In the last post we looked at how Thymeleaf handles reusable layouts. A reddit user enlightened me about an open source 'dialect' for Thymeleaf that makes it behave in a similar manner to SiteMesh. It's documented pretty well, but...
Yeah, documentation can be boring sometimes, so I thought I'd put together a quick post on how that would look in the Spark Java application I've been working with. It works well, but it suffers from an inability to pass model variables to the layout template. That's something that SiteMesh can handle (although in a less-than-elegant manner in my opinion) and in my view it is almost deal breaker. Something as simple as a nav menu that is common across all pages will ultimately require some dynamic data, and if we're going to need to
th:insert the fragment on every page, then what are we really gaining by using this style of layout template? I'll add how I'd think this could be handled at the end of this post, but first let's look at how to implement this dialect in the project.
The first step is to include the dialect, so in
Now modify your
compile group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '2.2.1'
main()method to add the dialect. The rest of the Bootstrap class looks the same as our last post (with the exception of a new/modified route that I've created for this feature called
Now create the layout itself - similar to how you'd do in SiteMesh:
A few items to note in the layout:
layoutnamespace. The dialect uses it's own namespace instead of the Thymeleaf
<head>element of the 'child' page, by default the 'layout' page
<head>content will be merged. This is configurable. Any child
<title>elements will replace the layout
<title>element, but you can tweak that behavior too.
<div layout:fragment="content"></div>. This is where what we define in the child layout will end up. Very SiteMesh-like.
As mentioned above, the
<div layout:fragment="content"> starting on line 10 will be what ends up getting populated in the layout template. But as I mentioned above, there's no easy way to pass menu content from the child to the layout (that I've found). Technically, this works in the layout:
But that depends on the 'menu' variable to be in the model on every single page. I'd normally make sure it was, but something about that approach just feels dirty to me. I'd much rather pass the variable from the child to the layout in some manner (here is how SiteMesh handles it). Even that approach feels a little wonky to me, which is why I think I prefer the approach used in my last post, but perhaps I'm missing a feature of this dialect.