Module 7: Frontend Basics (Optional for Beginners)

Let's bridge the gap between backend and frontend by visualizing our data with Spring Boot and Thymeleaf.

Backend vs. Frontend

So far, our Spring Boot application has acted as a backend API, returning raw JSON data for other applications to consume. In this optional module, we'll explore server-side rendering, where our Spring application generates and serves complete HTML pages directly to the user's browser.

7.1 Using Thymeleaf with Spring Boot

Thymeleaf is a modern server-side Java template engine. Think of it as "HTML on steroids." It allows you to embed logic and placeholders directly into your HTML files, which are then processed by the server before being sent to the browser.

Why Thymeleaf?

  • Seamless Spring Integration: It's the recommended template engine for Spring Boot.
  • Natural Templating: A Thymeleaf file is still a valid HTML file. You can open it in a browser and it will look like a normal webpage (a static prototype), which is great for designers.

Setup

Getting started is as simple as adding one dependency to your pom.xml. Spring Boot's autoconfiguration handles the rest.


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

By default, Spring Boot will now look for your HTML template files in the src/main/resources/templates/ directory.

7.2 Building a Simple HTML Page for Login

Let's create a user-friendly login page instead of relying on the default browser pop-up.

Step 1: The MVC Controller

We need a controller to handle the request for the login page. Note that we use @Controller, not @RestController. A standard @Controller returns a view name (the name of the HTML file), whereas a @RestController returns data (like JSON).


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class AuthController {

    // When a user goes to "/login", this method is called.
    @GetMapping("/login")
    public String loginPage() {
        // It returns the string "login". Spring Boot + Thymeleaf will look for
        // a file named "login.html" in src/main/resources/templates/
        return "login";
    }
}

Step 2: The Thymeleaf Template (`login.html`)

Create a file named login.html inside src/main/resources/templates/.


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Bank Login</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <h2>Login to Your Bank Account</h2>
        <!-- Spring Security will automatically handle this form submission -->
        <form th:action="@{/login}" method="post">
            <div class="mb-3">
                <label for="username" class="form-label">Username</label>
                <input type="text" id="username" name="username" class="form-control" required>
            </div>
            <div class="mb-3">
                <label for="password" class="form-label">Password</label>
                <input type="password" id="password" name="password" class="form-control" required>
            </div>
            <button type="submit" class="btn btn-primary">Log In</button>
        </form>
    </div>
</body>
</html>

The key part is th:action="@{/login}". The @{} syntax is Thymeleaf's way of creating URLs. This form will POST to the `/login` endpoint that Spring Security provides for processing authentication.

7.3 Displaying Customer Balance and Transaction History

After a user logs in, we want to show them their account information on a dashboard page.

Step 1: The Dashboard Controller

This controller will fetch the logged-in user's data and pass it to the Thymeleaf template using a Model object.


@Controller
public class DashboardController {

    @Autowired
    private AccountService accountService;

    @GetMapping("/dashboard")
    public String showDashboard(Model model, Authentication authentication) {
        // 'authentication.getName()' gives us the username of the logged-in user
        String username = authentication.getName();
        
        // We would have a service method to get accounts by username
        List<Account> accounts = accountService.getAccountsByUsername(username);
        
        // Add the data to the model. The template can now access "userName" and "userAccounts".
        model.addAttribute("userName", username);
        model.addAttribute("userAccounts", accounts);
        
        return "dashboard"; // Renders dashboard.html
    }
}

Step 2: The Dashboard Template (`dashboard.html`)

This template uses Thymeleaf attributes like th:text to display data and th:each to loop over collections.


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Account Dashboard</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container mt-5">
        <!-- Using th:text to display the userName attribute from the Model -->
        <h2 th:text="'Welcome back, ' + ${userName} + '!' ">Welcome!</h2>
        
        <h3 class="mt-4">Your Accounts</h3>
        
        <!-- Using th:if to check if the list of accounts is empty -->
        <div th:if="${#lists.isEmpty(userAccounts)}">
            <p>You don't have any accounts yet.</p>
        </div>
        
        <!-- Using th:unless to render the table only if the list is NOT empty -->
        <table class="table table-striped" th:unless="${#lists.isEmpty(userAccounts)}">
            <thead>
                <tr>
                    <th>Account Number</th>
                    <th>Balance</th>
                </tr>
            </thead>
            <tbody>
                <!-- Using th:each to loop over the userAccounts list -->
                <tr th:each="account : ${userAccounts}">
                    <td th:text="${account.id}">12345</td>
                    <td th:text="${#numbers.formatCurrency(account.balance)}">$1,000.00</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>