The Joy of Mustache: Server Side Templates for the JVM
Here are a few server side templates you can use for JVMs. See how Mustache can make your life a bit easier for you and, by extension, your users.
Join the DZone community and get the full member experience.
Join For FreeYou can load a home page and log into your application using a form. The user can’t yet log out, so you probably want to add that feature, ideally as a link on all pages, so that makes it part of the layout. To show how that works, let’s add a generic, declarative menu bar to the application, and make one part of it a logout button.
The logout link is actually pretty easy. We only need a form with the CSRF token and a link to submit it, e.g:
Layout.html
<!doctype html>
<html lang="en">
<head>
<title>{{{layout.title}}}</title>
</head>
<body>
<form id="logout" action="/logout" method="post">
<input type="hidden" name="_csrf" value="{{_csrf.token}}" />
<button type="submit" class="btn btn-primary">Logout</button>
</form>
{{{layout.body}}}
</body>
</html>
That already should work. But lets incorporate the logout into a more generic set of menu links. A list of elements in HTML can be represented as a <ul/>
with nested <li/>
, so the menus for your application can be rendered that way. In Mustache you do iteration just like lambdas, using a tag, so let’s invent a new one called {{#menus}}
:
Layout.html
<!doctype html>
<html lang="en">
<head>
<title>{{{layout.title}}}</title>
</head>
<body>
<ul class="nav nav-pills" role="tablist">
{{#menus}}<li><a href="{{path}}">{{name}}</a></li>{{/menus}}
<li><a href="#" onclick="document.getElementById('#logout').submit()">Logout</a></li>
</ul>
{{{layout.body}}}
<form id="logout" action="/logout" method="post">
<input type="hidden" name="_csrf" value="{{_csrf.token}}" />
</form>
</body>
</html>
Notice that inside the {{#menus}}
tag we pull out variables, "name" and "path" using the normal Mustache syntax.
Now you have to define the tag in your controller advice (or equivalently in the controllers), so that "menus" resolves to an iterable:
LayoutAdvice.java
@ModelAttribute("menus")
public Iterable<Menu> menus() {
return application.getMenus();
}
So this new code introduced a Menu
type that contains the static content for each menu in the UI. The layout calls for "name" and "path", so you need those properties:
Menu.java
class Menu {
private String name;
private String path;
// ... getters and setters
}
In the layout advice above, the menus came from an application
object. That wasn’t strictly necessary: you could have declared the list of menus inline in the menus()
method, but extracting it into another object gives us the chance to use a nice Spring Boot feature, where we can declare the menus in a config file in a compact format.
So now you need to create the Application
object to hold the menus, and inject it into the layout advice:
Layout.java
private Application application;
@Autowired
public LayoutAdvice(Compiler compiler, Application application) {
this.compiler = compiler;
this.application = application;
}
Where in Application
you have something like this
Application.java
@Component
@ConfigurationProperties("app")
class Application {
private List<Menu> menus = new ArrayList<>();
// .. getters and setters
}
The @ConfigurationProperties
tells Spring Boot to bind to this bean from the environment. Switching from application.properties
to application.yml
you could create a "Home" and a "Login" menu like this:
Application.yml
app.menus:
- name: Home
path: /
- name: Login
path: /login
With this in place, the "layout.html" that you already defined now has all it needs to work.
The sample code is tagged with "menus" at this point in GitHub, if you want to check it out and compare notes. It’s also the final state, so it’s the same code in master, possibly with bug fixes and updates to libraries. I hope you enjoy using Mustache as much as I do.
Published at DZone with permission of Dave Syer. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
How to Optimize CPU Performance Through Isolation and System Tuning
-
How To Integrate Microsoft Team With Cypress Cloud
-
How To Scan and Validate Image Uploads in Java
-
Mainframe Development for the "No Mainframe" Generation
Comments