Sunday, December 2, 2012

Spring and Thymeleaf with JavaConfig (Part 2)

Review


In the previous section, we have read the functional specs of the application. In this section, we will focus on the view layer, create an HTML mockup, and integrate our mockup with Thymeleaf.

Table of Contents

Click on a link to jump to that section:
  1. Functional Specs
  2. Creating the View
    • HTML Mockup
    • Thymeleaf Integration
  3. JavaConfig
    • ApplicationContext.java
    • SpringDataConfig.java
    • ThymeleafConfig.java
    • ApplicationInitializer.java
  4. Layers
    • Domain
    • Service
    • Controller
  5. Running the application
    • Clone from GitHub
    • Create the Database
    • Run with Maven and Tomcat 7
    • Run with Maven and Jetty 8
    • Import to Eclipse
    • Validate with W3C

Creating the View


In designing our application we'll start with the view layer because we can. Thanks to Thymeleaf creating HTML mockups is easy. Thymeleaf allows us to use these mockups as our HTML templates without any aesthetic changes. In addition, it passes W3C Markup Validation Service with flying colors.

What is Thymeleaf?

Thymeleaf is a Java library. It is an XML / XHTML / HTML5 template engine (extensible to other formats) that can work both in web and non-web environments. It is better suited for serving XHTML/HTML5 at the view layer of web applications, but it can process any XML file even in offline environments.

It provides an optional module for integration with Spring MVC, so that you can use it as a complete substitute of JSP in your applications made with this technology, even with HTML5.

The main goal of Thymeleaf is to provide an elegant and well-formed way of creating templates. Its Standard and SpringStandard dialects allow you to create powerful natural templates, that can be correctly displayed by browsers and therefore work also as static prototypes. You can also extend Thymeleaf by developing your own dialects.

Source: Thymeleaf.org

HTML Mockup


Let's create our HTML mockup. You can see the final mockup below:


First, we create a new HTML page. Note that this is a very simple HTML document that validates with W3C Markup Validation Service.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../resources/css/style.css"/>
<title>Title</title>
</head>
<body>
<div>
<table class="box-table-a">
<caption>Site Users</caption>
<thead>
<tr>
<th scope="col">Id</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Username</th>
<th scope="col">Role</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>George</td>
<td>Washington</td>
<td>gwash</td>
<td>admin</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
<tr>
<td>2</td>
<td>John</td>
<td>Adams</td>
<td>jadam</td>
<td>user</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
<tr>
<td>3</td>
<td>Thomas</td>
<td>Jefferson</td>
<td>tjeff</td>
<td>user</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
<tr>
<td>4</td>
<td>James</td>
<td>Madison</td>
<td>jmadi</td>
<td>admin</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
<tr>
<td>5</td>
<td>James</td>
<td>Monroe</td>
<td>jmonr</td>
<td>admin</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
</tbody>
</table>
<form action="#" method="post">
<table class="box-table-a">
<caption>New User</caption>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" hidden="hidden"/>
<input type="text"/></td>
<td><input type="text"/></td>
<td><input type="text"/></td>
<td><select>
<option value="1">Access Type 1</option>
<option value="2">Access Type 2</option>
</select></td>
</tr>
<tr>
<td>
<button type="submit">Action</button>
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</form>
</div>
</body>
</html>
view raw users.html hosted with ❤ by GitHub


Next, we create an external CSS file. Because I'm not really a designer, I have scoured the web for a simple but elegant table style. I found one from Top 10 CSS Table Designs.

body {
font-family: Arial, sans-serif;
font-size: 1em;
}
caption {
font-size: 18px;
}
/**
* Table style from Top 10 CSS Table Designs
* http://coding.smashingmagazine.com/2008/08/13/top-10-css-table-designs/
*/
.box-table-a {
font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif;
font-size: 12px;
margin: 45px;
width: 700px;
text-align: left;
border-collapse: collapse;
}
.box-table-a th {
font-size: 13px;
font-weight: normal;
padding: 8px;
background: #b9c9fe;
border-top: 4px solid #aabcfe;
border-bottom: 1px solid #fff;
color: #039;
}
.box-table-a td {
padding: 8px;
background: #e8edff;
border-bottom: 1px solid #fff;
color: #669;
border-top: 1px solid transparent;
}
.box-table-a tr:hover td {
background: #d0dafd;
color: #339;
}
view raw styles.css hosted with ❤ by GitHub


Then, open a browser and test the HTML mockup. You should see something similar to the following image:



Thymeleaf Integration


It's time to integrate Thymeleaf with our HTML mockup template. To integrate Thymeleaf we'll use its attribute-based template engine. Browsers will normally ignore unknown HTML attributes, so it won't affect our mockups.

Before we proceed, let me provide you a short description of the specific Thymeleaf attributes we'll be using:

The important attributes
  • The # means to resolve the attribute from the messages bundle
  • The $ means to resolve the attribute from the model
  • The # and $ can be combined together so that messages can be dynamically generated from the model and internationalized from the messages bundle
  • th:fragment="header"

    This allows us to include template fragments from other templates. For example, we can reuse them in footers, headers, and menus. For this tutorial, we won't be reusing the header, but I've added it anyway for future tutorials.
  • th:each="u : ${users}

    This allows us to loop a list of records. This is equivalent to Java's for-loop construct.
  • th:text="${u.id}"

    This allows to dynamically set the label of an element.
  • th:href="@{/users/delete(id=${u.id})}">

    This allows us to define a dynamic URL.
  • th:field="*{id}"

    This allows us to define the field where an input's field will be attached to.
  • th:remove="all"

    This allows us to setup mockup data. Thymeleaf will automatically remove any element contained within this attribute.

Let's now apply these attributes. Here's our updated HTML mockup template:

users.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head th:fragment="header">
<meta charset="utf-8" />
<link rel="stylesheet" href="../../resources/css/style.css" th:href="@{/resources/css/style.css}" />
<title th:text="#{user.page.title}">Title</title>
</head>
<body>
<div>
<table class="box-table-a">
<caption th:text="#{user.table.caption}">Site Users</caption>
<thead>
<tr>
<th scope="col" th:text="#{user.id.label}">Id</th>
<th scope="col" th:text="#{user.firstname.label}">First Name</th>
<th scope="col" th:text="#{user.lastname.label}">Last Name</th>
<th scope="col" th:text="#{user.username.label}">Username</th>
<th scope="col" th:text="#{user.role.label}">Role</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<tr th:each="u : ${users}">
<td th:text="${u.id}">1</td>
<td th:text="${u.firstName}">George</td>
<td th:text="${u.lastName}">Washington</td>
<td th:text="${u.username}">gwash</td>
<td th:text="#{${'user.role.' + u.role}}">admin</td>
<td><a href="#" th:href="@{/users/delete(id=${u.id})}">del</a> |
<a href="#" th:href="@{/users/edit(id=${u.id})}">edit</a>
</td>
</tr>
<tr th:remove="all">
<td>2</td>
<td>John</td>
<td>Adams</td>
<td>jadam</td>
<td>user</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
<tr th:remove="all">
<td>3</td>
<td>Thomas</td>
<td>Jefferson</td>
<td>tjeff</td>
<td>user</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
<tr th:remove="all">
<td>4</td>
<td>James</td>
<td>Madison</td>
<td>jmadi</td>
<td>admin</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
<tr th:remove="all">
<td>5</td>
<td>James</td>
<td>Monroe</td>
<td>jmonr</td>
<td>admin</td>
<td><a href="#">del</a> | <a href="#">edit</a></td>
</tr>
</tbody>
</table>
<form action="#" th:action="@{/users/create}" th:object="${commanduser}" method="post">
<table class="box-table-a">
<caption th:text="#{${usertype + '.user.table.caption'}}">New User</caption>
<thead>
<tr>
<th th:text="#{user.firstname.label}">First Name</th>
<th th:text="#{user.lastname.label}">Last Name</th>
<th th:text="#{user.username.label}">Username</th>
<th th:text="#{user.role.label}">Role</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" hidden="hidden" th:field="*{id}"/>
<input type="text" th:field="*{firstName}"/></td>
<td><input type="text" th:field="*{lastName}"/></td>
<td><input type="text" th:field="*{username}"/></td>
<td><select th:field="*{role}">
<option th:each="role : ${allRoles}" th:value="${role}"
th:text="#{${'user.role.' + role}}">Access Type 1</option>
<option th:remove="all" value="2">Access Type 2</option>
</select></td>
</tr>
<tr>
<td>
<button type="submit" th:text="#{${usertype + '.user.button.label'}}">Action</button>
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</form>
</div>
</body>
</html>
view raw users.html hosted with ❤ by GitHub

Internationalization


The th:text attribute allows us to externalize text and with the support of Spring's MessageSource, we are able to parameterize and provide internationalization support.

What is MessageSource?

Strategy interface for resolving messages, with support for the parameterization and internationalization of such messages.

Spring provides two out-of-the-box implementations for production:
  • ResourceBundleMessageSource, built on top of the standard ResourceBundle
  • ReloadableResourceBundleMessageSource, being able to reload message definitions without restarting the VM

Source: Spring 3 Docs: MessageSource

Notice the th:text attributes. Some of them refer to a dot notation object. Where does Thymeleaf retrieve this information?

The information is retrieved from the messages_en.properties resource bundle:

user.page.title=User Management
user.table.caption=Site Users
user.id.label=Id
user.firstname.label=First Name
user.lastname.label=Last Name
user.username.label=Username
user.role.label=Role
user.role.1=admin
user.role.2=regular
new.user.table.caption=New User
new.user.button.label=Add User
update.user.table.caption=Existing User
update.user.button.label=Update User

We've declared that in the ApplicationContext.java configuration (see next section):

// Provides internationalization of messages
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("messages");
return source;
}


The Data transfer object (DTO)


In order for our html page to display data from the Controller, we need to pass a Model attribute. The model attribute is represented by the UserDto.

package org.krams.response;
import java.io.Serializable;
public class UserDto implements Serializable {
private Long id;
private String firstName;
private String lastName;
private String username;
private Integer role;
...getters/setters...
}
view raw UserDto.java hosted with ❤ by GitHub


The fields we declared on the users.html form is based from the fields of the UserDto:

<form action="#" th:action="@{/users/create}" th:object="${commanduser}" method="post">
....
<tr>
<td><input type="text" hidden="hidden" th:field="*{id}"/>
<input type="text" th:field="*{firstName}"/></td>
<td><input type="text" th:field="*{lastName}"/></td>
<td><input type="text" th:field="*{username}"/></td>
<td><select th:field="*{role}">
<option th:each="role : ${allRoles}" th:value="${role}"
th:text="#{${'user.role.' + role}}">Access Type 1</option>
<option th:remove="all" value="2">Access Type 2</option>
</select></td>
</tr>
....
</form>
view raw users.html hosted with ❤ by GitHub


Notice the form has a form-backing object declared named commanduser. Using Thymeleaf's attribute th:object, we're able to declare this form-backing object.

This object passed from the UserController.

@Controller
@RequestMapping("/users")
public class UserController {
@RequestMapping
public String getUsersPage(ModelMap model) {
Pageable pageRequest = new PageRequest(0, 10);
Page<User> users = repository.findAll(pageRequest);
model.addAttribute("users", UserMapper.map(users));
model.addAttribute("commanduser", new UserDto());
model.addAttribute("usertype", "new");
return "users";
}
}


Next

In the next section, we will focus on the configuration layer. We'll study how to declare a JavaConfig-based configuration. We'll also provide an XML-based configuration for comparison purposes. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring and Thymeleaf with JavaConfig (Part 2) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

2 comments:

  1. I see you made the head a fragment. How are you going to use that fragment and variablize the title so it can be used on other pages? I'm trying to get that part worked out. Welcoming input on how to do that.

    ReplyDelete
  2. Thank you for your post, I look for such article along time, today i find it finally. this post give me lots of advise it is very useful for me. Find out today's Celebrity birthdays and discover who shares your birthday. We make it simple and entertaining to learn about celebrities.

    ReplyDelete