-
Notifications
You must be signed in to change notification settings - Fork 8
04 Creating Books
Now, that we have a book class as well as all the backend code to create and retrieve books, let's add a page to create new books.
First, let's create a controller that backs the page we are about to create. Typically, there should be one controller per page. Our page will just be a form to enter a title and author. In the web package, add the following class:
package edu.asu.diging.springaction.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import edu.asu.diging.springaction.core.model.impl.BookImpl;
import edu.asu.diging.springaction.core.service.BookManager;
@Controller
public class AddBookController {
@Autowired
private BookManager bookManager;
@RequestMapping(value="admin/book/add", method=RequestMethod.GET)
public String show(Model model) {
model.addAttribute("book", new BookImpl());
return "admin/books/add";
}
}
There are three annotations here:
-
@Controller
: We've seen this one before. It tells Spring that this is a controller class. It will be scanned for@RequestMapping
annotations. -
@RequestMapping
: This annotation tells Spring that when a GET request toadmin/book/add
comes in (e.g. when you go to http://localhost:8080/springtoaction/admin/book/add), then the methodshow
should be called. -
@Autowired
: This one shouldn't be new either. It tells Spring that we want an instance of a class that implements the interfaceBookManager
.
As you can see, the show
method returns a string "admin/books/add"
. Like in the HomeController
, this is the path to the template that should be rendered.
First create a folder books
in src/main/webapp/WEB-INF/views/admin
. Then create a file called add.html
inside the books
folder. Add the following to the file:
<html layout:decorate="~{layouts/main}">
<head>
<title>Library App</title>
</head>
<body>
<div layout:fragment="content">
<form action="#" th:action="@{/admin/book/add}" method="POST" th:object="${book}">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<label>Title: </label>
<input th:field="*{title}" class="form-control input-sm"></input>
<p class="text-danger"><th:errors path="title"/></p>
<label>Author: </label>
<input th:field="*{author}" class="form-control input-sm"></input>
<p class="text-danger"><th:errors path="author"/></p>
<div style="padding-top: 20px;">
<button class="btn btn-sm btn-primary pull-right">Add Book</button>
</div>
</form>
</div>
</body>
</html>
This template will create a simple form with two input fields (title and author) and a button to submit the form. The attribute th:object="${book}"
of the form
tag tells Thymeleaf that the form is backed by that object. Whenever we use the star-notation within the form (e.g. th:field="*{authors}"
), Thymeleaf will now look for the attribute on the book object.
Now, let's see if we can see our work! Start the server and go to http://localhost:8080/spring-to-action/admin/book/add. You should see a login page. Login with your admin account. You should now see the form.
If you hit the "Add Book" button, however, you will get a "HTTP Status 405 – Method Not Allowed" error. This happens because our controller only defines a method for GET requests but we are making a POST request. So, let's add a method to handle our POST requests.
@Controller
public class AddBookController {
@Autowired
private BookManager bookManager;
@RequestMapping(value="admin/book/add", method=RequestMethod.GET)
public String show(Model model) {
model.addAttribute("book", new BookImpl());
return "admin/books/add";
}
@RequestMapping(value="admin/book/add", method=RequestMethod.POST)
public String add(@ModelAttribute("book") BookImpl book) {
bookManager.store(book.getAuthor(), book.getTitle());
return "redirect:/admin/book/add";
}
}
Now, if you click the "Add Book" button, you should be redirected to the add book form and a new book should be stored in the database. In the second method we added, you can see another @RequestMapping
annotation that tells Spring the add
method should be called when POST requests to admin/book/add
come in. It maps the values in the form to a new BookImpl
object and after storing the book in the database the user is redirected to /admin/book/add
.
Before we move on to borrowing books, let's add all books to our home page. Open the HomeController
and modify it like this:
@Controller
public class HomeController {
@Autowired
private BookManager bookManager;
@RequestMapping(value = "/")
public String home(Model model) {
model.addAttribute("books", bookManager.all());
return "home";
}
}
You can see that we are now retrieving all books and putting them into the model. Now, we just need to show them on the home.html
page. Remove the jumbotron and add a list of books instead:
<html layout:decorate="~{layouts/main}">
<head>
<title>Authority Matcher</title>
</head>
<body>
<div layout:fragment="content">
<ul class="list-group">
<li th:each="book : ${books}" class="list-group-item clearfix">
<i>[[${book.title}]]</i> by <b>[[${book.author}]]</b>
</div>
</li>
</ul>
</div>`
</body>
</html>
You can see that there is a loop iterating over all books in the model attribute "books": <li th:each="book : ${books}" class="list-group-item clearfix">
. This will create a new li
element for each book. You might also notice that instead of using th:text
, we use [[${book.title}]]
. This notation is an alternative to the th:text
attribute. Anything inside the square brackets will be interpreted by Thymeleaf.
If you stopped your server for some reason, you do not have to go back to the context menu of your project and choose "Run on Server". You can simply open the server tab of Eclipse (Window > Show View > Other...) and right-click on your server to start, stop or restart it.
When you edit a Thymeleaf template, you do not restart the server to see your changes. Simply reload the page. If the page shows up blank (all of it or partially), that typically means you have an error in your template. Thymeleaf stops rendering a page when it encounters an error. You can find Thymeleaf's documentation here.
Digital Innovation Group @ ASU - DigInG Spring Training - © 2022