Listing 7. widget.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Widget Details</title>
<link href="../static/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
</head>
<body>
<div class="container">
<ol class="breadcrumb">
<li><a href="#" th:href="${'/widgets'}">Home</a></li>
</ol>
<div class="row"><h2>Widget Details</h2></div>
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">ID:</label>
<div class="col-sm-10">
<p class="form-control-static" th:text="${widget.id}">Widget ID</p>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Name:</label>
<div class="col-sm-10">
<p class="form-control-static" th:text="${widget.name}">Widget Name</p>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Description:</label>
<div class="col-sm-10">
<p class="form-control-static" th:text="${widget.description}">Widget Description</p>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<a href="#" class="btn btn-default" th:href="${'/widget/edit/' + widget.id}">Edit</a>
</div>
</div>
</form>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="../static/js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script>
</body>
</html>
Aside from the Bootstrap markup, this HTML file shows the three widget fields in paragraph elements using the th:text
Thymeleaf attribute with the ${widget.propertyName}
notation. If you are not familiar with Bootstrap, don't let the form trick you into thinking this is a proper HTML form that accepts fields; it is just a layout paradigm. You can see sample Bootstrap forms here.
The only other two things I added are:
- A breadcrumb that allows the user to navigate back to the homepage (widget list), using an anchor tag with the
th:href
attribute that points to the/widgets
page. - A button (disguised as an anchor tag but rendered as a button using the Bootstrap class
btn btn-default
CSS) with ath:href
attribute pointing to${'/widget/edit' + widget.id}
. This notation performs aString
concatenation of/widget/edit/
with the ID of the widget, resulting in something like/widget/edit/1
.
An example of this page is shown in Figure 5.
Figure 5. Widget edit page
If the user presses the Edit button, the editWidget
method is called:
@GetMapping("/widget/edit/{id}")
public String editWidget(@PathVariable Long id, Model model) {
model.addAttribute("widget", widgetRepository.findById(id).orElse(new Widget()));
return "widgetform";
}
This method maps to the /widget/edit/{id}
URI, retrieves the widget from the database, sets it as the model's "widget" attribute, and tells Spring to render it using the "widgetform.html" Thymeleaf template, shown in Listing 8.
Listing 8. widgetform.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Create/Edit Widget</title>
<link href="../static/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
</head>
<body>
<h2>Create / Edit Widget</h2>
<div class="container">
<form th:object="${widget}" th:action="@{/widget}" method="post">
<input type="hidden" th:field="*{id}" />
<div class="form-group">
<label>Name</label>
<input type="text" th:field="*{name}" class="form-control" placeholder="Name">
</div>
<div class="form-group">
<label>Description</label>
<input type="text" th:field="*{description}" class="form-control" placeholder="Description">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="../static/js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script>
</body>
</html>
This template introduces a proper Thymeleaf form. To build a form with Thymeleaf, you specify the object that the form is operating on, using the th:object
attribute of the form
, and then you can access that object's properties using th:field
attribute in form elements. These field attributes are accessed using the "*{propertyName}
" notation, which refers back to the form's object. In this example, we define a hidden text field to hold the ID (we don't want the user changing the primary key in the database) and we add the name and description fields. At the end of the document we add a submit button, which submits the form to the th:action
, which points to "@{/widget}
". Notice that we use the @
symbol to reference locations and the $
symbol to reference model objects.
We are able to leverage the "widgetform" for both creating new Widgets and editing existing Widgets by adding an empty Widget
object in the newWidget
method:
@GetMapping("/widget/new")
public String newWidget(Model model) {
model.addAttribute("widget", new Widget());
return "widgetform";
}
Because we pass an empty Widget
object through the "widget" model attribute, the form will not fail if a Widget is not present. This allows us to reuse the same form both for creating and editing widgets.
Now that we have our controller and templates defined, we're ready to run the application. To run a Spring Boot application, execute the following Maven command:
mvn spring-boot:run
You should see Spring start Tomcat, create an embedded H2 database with the name testdb
and then show a summary of the URI mappings for your WidgetController
.
To see your application in action, open a browser to the following URL:
http://localhost:8080/widgets
When you are ready to release and run your application in production, you can build your project with the standard mvn clean install
and then run the resultant executable JAR file:
$ mvn clean install
$ java -jar target/spring5mvc-example-0.0.1-SNAPSHOT.jar
Conclusion
Spring MVC is the traditional Spring framework library for building Java web applications. This article introduced you to Spring MVC web development using Spring Boot, Spring Initializr, and the Thymeleaf view rendering engine.
There's lots more to learn about Spring framework 5! Visit the second half of this tutorial to get started with reactive programming in the Spring framework, using Spring WebFlux annotations and functional programming techniques.