STAGE 7 OF 37

Stage 7: apply Model and Thymeleaf to the calculator

Learning path / Stage 7 of 37

Before you start: use JDK 21+ and Maven 3.9+. Extract this stage’s project and open a terminal in its pom.xml folder. Complete the setup below, including a database connection where required, then run mvn spring-boot:run. Use the port specified for this stage and stop with Ctrl+C.

Use two input fields and five buttons. A static form sends numbers to Java; the controller calculates the answer and selects a result template.

Download spring-calculator.zip

Follow the request
Form -> GET /calculate?a=20&b=6&operation=remainder
 -> controller calculates 2.0
 -> Model carries values
 -> Thymeleaf renders result.html
 -> browser displays a separate result page

The two inputs submit a and b. Only the clicked button submits its operation value. @RequestParam reads these three values. Java chooses an operation using switch. Model carries named values from the controller to the template. return "result" selects templates/result.html; it does not send the literal word result. Thymeleaf replaces th:text expressions with the supplied values. There is no @ResponseBody on this method. Remainder (%) means what is left after division, not percentage. The result page includes messages for zero divisors.

Try and verify: Enter 20 and 6, then click each button. Expect 26.0, 14.0, 120.0, about 3.3333333333333335 and 2.0. Try division by zero, then Calculate again. The project uses double, so decimal rounding artifacts can occur.

Open the files to study in this stage
src/main/resources/static/index.html — complete file
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Arithmetic Calculator</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
<main>
    <h1>Arithmetic Calculator</h1>
    <p>Enter two numbers and choose an operation.</p>

    <form action="/calculate" method="get">
        <label for="a">First number</label>
        <input id="a" name="a" type="number" step="any" required>

        <label for="b">Second number</label>
        <input id="b" name="b" type="number" step="any" required>

        <div class="buttons">
            <button type="submit" name="operation" value="add">Add (+)</button>
            <button type="submit" name="operation" value="subtract">Subtract (-)</button>
            <button type="submit" name="operation" value="multiply">Multiply (×)</button>
            <button type="submit" name="operation" value="divide">Divide (÷)</button>
            <button type="submit" name="operation" value="remainder">Remainder (%)</button>
        </div>
    </form>
    <p class="note">Remainder means what is left after division: 20 % 6 = 2.</p>
</main>
</body>
</html>
src/main/java/com/edupoly/calculator/CalculatorController.java — complete file
package com.edupoly.calculator;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class CalculatorController {
    @GetMapping("/calculate")
    public String calculate(
            @RequestParam("a") double first,
            @RequestParam("b") double second,
            @RequestParam("operation") String operation,
            Model model) {

        if (!Double.isFinite(first) || !Double.isFinite(second)) {
            model.addAttribute("error", "Please enter two finite numbers.");
            return "result";
        }

        double answer;
        String symbol;

        switch (operation) {
            case "add":
                answer = first + second;
                symbol = "+";
                break;
            case "subtract":
                answer = first - second;
                symbol = "-";
                break;
            case "multiply":
                answer = first * second;
                symbol = "×";
                break;
            case "divide":
                if (second == 0) {
                    model.addAttribute("error", "Cannot divide by zero.");
                    return "result";
                }
                answer = first / second;
                symbol = "÷";
                break;
            case "remainder":
                if (second == 0) {
                    model.addAttribute("error", "Cannot calculate a remainder with zero as the divisor.");
                    return "result";
                }
                answer = first % second;
                symbol = "%";
                break;
            default:
                model.addAttribute("error", "Please choose one of the five operations.");
                return "result";
        }

        if (!Double.isFinite(answer)) {
            model.addAttribute("error", "The result is too large. Please use smaller numbers.");
            return "result";
        }

        model.addAttribute("first", first);
        model.addAttribute("second", second);
        model.addAttribute("symbol", symbol);
        model.addAttribute("answer", answer);
        return "result";
    }
}
src/main/resources/templates/result.html — complete file
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Calculation Result</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
<main>
    <h1>Calculation Result</h1>

    <div th:if="${error == null}">
        <p class="equation">
            <span th:text="${first}">10</span>
            <span th:text="${symbol}">+</span>
            <span th:text="${second}">5</span>
            =
            <strong id="answer" th:text="${answer}">15</strong>
        </p>
    </div>

    <p id="error" class="error" th:if="${error != null}" th:text="${error}"></p>

    <a href="/">Calculate again</a>
</main>
</body>
</html>

The ZIP contains every setup file and a README.html with instructions. You can run the supplied project directly.

After Stage 7: the Spring words to recognize

Focus: Reuse the Model and template concepts from the product catalog.

TermOur concrete exampleIts place in Spring
Model attributes — reusedThe controller supplies first, second, symbol and answer to result.html.The view receives named calculation values instead of direct response text.
Conditional renderingth:if selects the answer section or the error message.Thymeleaf includes the relevant HTML according to a condition.
th:text — reusedThe answer appears where result.html uses th:text="${answer}".Thymeleaf writes a supplied value as escaped HTML text.

You should now be able to say: Java calculates; Model carries the values; the result template presents the answer or error.

One small check: Which line calculates the answer, which line adds it to Model, and which HTML attribute displays it?

Concepts used in this stage

Use the right column for a quick reminder, or open a concept above for its full explanation.

Java 21 · Spring Boot 4.1.1 · Edupoly