OutOfMemoryError
OutOfMemoryError

Reputation: 421

Spring - Thymeleaf: Exception processing template

I'm following the a book Spring in Action 5th ed., but I believe that is presents a bug.

This is the GitHub of the book. I'm arrived on chap 3 tacos-jdbc source of code

When submit my order a sudden error appears:

enter image description here

and in this way on the terminal:

2019-05-25 16:58:18.164 ERROR 11777 --- [nio-8080-exec-7] org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-8080-exec-7] Exception processing template "orderForm": An error happened during template parsing (template: "class path resource [templates/orderForm.html]")

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/orderForm.html]")

enter image description here

OrderController:

@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
public class OrderController {

    private OrderRepository orderRepo;

    public OrderController(OrderRepository orderRepo) {
        this.orderRepo = orderRepo;
    }

    @GetMapping("/current")
    public String orderForm() {
        return "orderForm";
    }

    @PostMapping
    public String processOrder(@Valid Order order, Errors errors,
                               SessionStatus sessionStatus) {
        if (errors.hasErrors()) {
            return "orderForm";
        }

        orderRepo.save(order);
        sessionStatus.setComplete();

        return "redirect:/";
    }

}

and orderForm:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Taco Cloud</title>
    <link rel="stylesheet" th:href="@{/styles.css}" />
</head>

<body>

<form method="POST" th:action="@{/orders}" th:object="${order}">
    <h1>Order your taco creations!</h1>

    <img th:src="@{/images/TacoCloud.png}"/>
    <a th:href="@{/design}" id="another">Design another taco</a><br/>

    <div th:if="${#fields.hasErrors()}">
        <span class="validationError">
        Please correct the problems below and resubmit.
        </span>
    </div>

    <h3>Deliver my taco masterpieces to...</h3>
    <label for="name">Name: </label>
    <input type="text" th:field="*{name}"/>
    <br/>

    <label for="street">Street address: </label>
    <input type="text" th:field="*{street}"/>
    <br/>

    <label for="city">City: </label>
    <input type="text" th:field="*{city}"/>
    <br/>

    <label for="state">State: </label>
    <input type="text" th:field="*{state}"/>
    <br/>

    <label for="zip">Zip code: </label>
    <input type="text" th:field="*{zip}"/>
    <br/>

    <label for="ccNumber">Credit Card #: </label>
    <input type="text" th:field="*{ccNumber}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('ccNumber')}"
          th:errors="*{ccNumber}">CC Num Error</span>

    <label for="ccExpiration">Expiration: </label>
    <input type="text" th:field="*{ccExpiration}"/>
    <br/>

    <label for="ccCVV">CVV: </label>
    <input type="text" th:field="*{ccCVV}"/>
    <br/>

    <input type="submit" value="Submit order"/>
</form>


</body>
</html>

Upvotes: 7

Views: 16153

Answers (4)

Marquise Mery
Marquise Mery

Reputation: 140

I think you should handle the display of the validation errors in all the input in the orderForm.html as mentioned in the book as following: (Source Git)

<!-- tag::allButValidation[] -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
  <head>
    <title>Taco Cloud</title>
    <link rel="stylesheet" th:href="@{/styles.css}" />
  </head>

  <body>

    <form method="POST" th:action="@{/orders}" th:object="${order}">
      <h1>Order your taco creations!</h1>

      <img th:src="@{/images/TacoCloud.png}"/>
      <a th:href="@{/design}" id="another">Design another taco</a><br/>

      <div th:if="${#fields.hasErrors()}">
        <span class="validationError">
        Please correct the problems below and resubmit.
        </span>
      </div>

      <h3>Deliver my taco masterpieces to...</h3>
      <label for="name">Name: </label>
      <input type="text" th:field="*{name}"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="${#fields.hasErrors('name')}"
            th:errors="*{name}">Name Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="street">Street address: </label>
      <input type="text" th:field="*{street}"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="${#fields.hasErrors('street')}"
            th:errors="*{street}">Street Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="city">City: </label>
      <input type="text" th:field="*{city}"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="${#fields.hasErrors('city')}"
            th:errors="*{city}">City Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="state">State: </label>
      <input type="text" th:field="*{state}"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="${#fields.hasErrors('state')}"
            th:errors="*{state}">State Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="zip">Zip code: </label>
      <input type="text" th:field="*{zip}"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="${#fields.hasErrors('zip')}"
            th:errors="*{zip}">Zip Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <h3>Here's how I'll pay...</h3>
<!-- tag::validatedField[] -->
      <label for="ccNumber">Credit Card #: </label>
      <input type="text" th:field="*{ccNumber}"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="${#fields.hasErrors('ccNumber')}"
            th:errors="*{ccNumber}">CC Num Error</span>
<!-- tag::allButValidation[] -->
<!-- end::validatedField[] -->
      <br/>

      <label for="ccExpiration">Expiration: </label>
      <input type="text" th:field="*{ccExpiration}"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="${#fields.hasErrors('ccExpiration')}"
            th:errors="*{ccExpiration}">CC Num Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="ccCVV">CVV: </label>
      <input type="text" th:field="*{ccCVV}"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="${#fields.hasErrors('ccCVV')}"
            th:errors="*{ccCVV}">CC Num Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <input type="submit" value="Submit order"/>
    </form>

  </body>
</html>
<!-- end::allButValidation[] -->

I think you are not inserting the right information in the form according to validation rules added in the beans as explained in the chapter. By displaying the validation error, you will know exactly which input is not correctly inserted when you submit the order.


After investigating your code. The name of attributes in the Order.java are not the same as in the view page orderForm.html

For example, in orderForm the attribute is name

  <h3>Deliver my taco masterpieces to...</h3>
    <label for="name">Name: </label>
    <input type="text" th:field="*{name}"/>

Whereas in Order.java is deliveryName.

 @NotBlank(message="Delivery name is required")
    private String deliveryName;

The solution is to use the same name of the attribute in the Order.java and orderForm.html page.

Upvotes: 0

OutOfMemoryError
OutOfMemoryError

Reputation: 421

I fixed with this code adding delivery prefix:

    <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Taco Cloud</title>
    <link rel="stylesheet" th:href="@{/styles.css}" />
</head>

<body>

<form method="POST" th:action="@{/orders}" th:object="${order}">
    <h1>Order your taco creations!</h1>

    <img th:src="@{/images/TacoCloud.png}"/>

    <h3>Your tacos in this order:</h3>
    <a th:href="@{/design}" id="another">Design another taco</a><br/>
    <ul>
        <li th:each="taco : ${order.tacos}"><span th:text="${taco.name}">taco name</span></li>
    </ul>

    <div th:if="${#fields.hasErrors()}">
        <span class="validationError">
        Please correct the problems below and resubmit.
        </span>
    </div>

    <h3>Deliver my taco masterpieces to...</h3>
    <label for="deliveryName">Name: </label>
    <input type="text" th:field="*{deliveryName}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('deliveryName')}"
          th:errors="*{deliveryName}">Name Error</span>
    <br/>

    <label for="deliveryStreet">Street address: </label>
    <input type="text" th:field="*{deliveryStreet}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('deliveryStreet')}"
          th:errors="*{deliveryStreet}">Street Error</span>
    <br/>

    <label for="deliveryCity">City: </label>
    <input type="text" th:field="*{deliveryCity}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('deliveryCity')}"
          th:errors="*{deliveryCity}">City Error</span>
    <br/>

    <label for="deliveryState">State: </label>
    <input type="text" th:field="*{deliveryState}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('deliveryState')}"
          th:errors="*{deliveryState}">State Error</span>
    <br/>

    <label for="deliveryZip">Zip code: </label>
    <input type="text" th:field="*{deliveryZip}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('deliveryZip')}"
          th:errors="*{deliveryZip}">Zip Error</span>
    <br/>

    <h3>Here's how I'll pay...</h3>
    <label for="ccNumber">Credit Card #: </label>
    <input type="text" th:field="*{ccNumber}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('ccNumber')}"
          th:errors="*{ccNumber}">CC Num Error</span>
    <br/>

    <label for="ccExpiration">Expiration: </label>
    <input type="text" th:field="*{ccExpiration}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('ccExpiration')}"
          th:errors="*{ccExpiration}">CC Num Error</span>
    <br/>

    <label for="ccCVV">CVV: </label>
    <input type="text" th:field="*{ccCVV}"/>
    <span class="validationError"
          th:if="${#fields.hasErrors('ccCVV')}"
          th:errors="*{ccCVV}">CC Num Error</span>
    <br/>

    <input type="submit" value="Submit order"/>
</form>

</body>
</html>

Upvotes: 3

You are using th:object="${order}" in your orderForm template but Thymeleaf doesn't know about it. You have to let Thymeleaf know about this object by passing it to the template like so

@GetMapping("/current")
public ModelAndView orderForm() {
    ModelAndView mv = new ModelAndView("orderForm");
    mv.addObject("order", new Order());
    return mv;
}

Note: You will have to pass the object from your controller layer at all the places where you are using that object in the template.


Update 1

Also update you post method

@PostMapping
public ModelAndView processOrder(@Valid Order order, Errors errors,
                           SessionStatus sessionStatus) {
    if (errors.hasErrors()) {
        ModelAndView mv = new ModelAndView("orderForm");
        mv.addObject("order", new Order());
        return mv;
    }

    orderRepo.save(order);
    sessionStatus.setComplete();

    return new ModelAndView("redirect:/");
}

Upvotes: 0

Dirk Deyne
Dirk Deyne

Reputation: 6936

I think you could do it this way

@Controller
public class OrderController {

   @GetMapping("/orders")
   public String orders(Order order) {
      return "orderForm";
   }

   @PostMapping("/orders")
   public String orderForm(@Valid Order order, BindingResult result, Model model) {
       if(result.hasErrors()) {
           return "orderForm";
       } else {
           retrun "your_success_view";   
       }
    }
}

Upvotes: 0

Related Questions