Spring Boot+Thymeleaf: th not able to resolve a Spring EL expression - spring

I am using spring boot+thymeleaf+neo4j. Everything is working fine except that thymeleaf is not able to resolve a few of the attributes of the 'product' variable used in the th:each block in the template product_grid.html, which includes th:src="${product.URL}", th:text="${Product.title}" and the th:action="#{/product/(${Product.getId()})}" expression in form tag. The th:text="${Product.Price}" is working. When I check the code produced in the browser the src tag is empty (src:""), the text attribute containing the title tag is not shown in the browser. The th:action works fine but when I click the button defined inside the form, the url changes to http://localhost:8080/product/?btn=View+Product
instead of the following code which is shown in the browser console
http://localhost:8080/product/?1
Note: I am trying to get the image url from a field which is stored in neo4j database. The project directory is:
project directory image
Template:product_grid.html
<html xmlns:th="http://www.thymeleaf.org" >
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Products</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.bundle.min.js" integrity="sha384-feJI7QwhOS+hwpX2zkaeJQjeiwlhOP+SdQDqhgvvo1DsjtiSQByFdThsxO669S2D"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="#">Grada</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item ">
<a class="nav-link" href="#">Home
<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link">My Best Products</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="#{/login}">Login</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<a class="btn btn-outline-success my-2 my-sm-0" href="file:///home/madhav/SPM/Grada/public_html/product.html">Search</a>
</form>
</div>
</nav>
<div class="container text-center">
<div class="row">
<div th:each="Product:${products}" class="col-lg-3 col-sm-12 col-md-6 my-2 p-auto">
<div class="card">
<div class="card-body">
<img src="http://via.placeholder.com/150x150/888/111" th:src="${Product.URL}" alt="img" class="card-img-top img-thumbnail img-fluid">
<div class="card-title lead" th:text="${Product.title}">Some product name</div>
<div class="card-text">Price: ₹<span th:text="${Product.Price}">400</span></div>
</div>
<form method="GET" action="/" th:action="#{/product/(${Product.getId()})}">
<input type="submit" name="btn" class="form-control btn btn-primary" value="View Product">
<input type="submit" name="btn" class="form-control btn btn-primary" value="Add to Cart">
</form>
</div>
</div>
</div>
</div>
</body>
</html>`
Product model:Product.html
package com.grada.ecommerce.Models;
import com.grada.ecommerce.Models.Seller;
import org.neo4j.ogm.annotation.*;
import java.util.HashSet;
import java.util.Set;
#NodeEntity(label = "Product")
public class Product
{
public Product()
{
}
public Product(String title, Double price, int quantity, float rating, String description, String url, String company)
{
this.title = title;
this.Rating = rating;
this.Description = description;
this.Price = price;
this.Quantity = quantity;
this.URL = url;
this.Company = company;
}
#Id
#GeneratedValue
private Long id;
#Property(name = "title")
public String title;
#Property(name = "Rating")
public float Rating;
#Property(name = "Description")
public String Description;
#Property(name = "Price")
public Double Price;
#Property(name = "Quantity")
public int Quantity;
#Property(name = "Company")
public String Company;
#Property(name = "URL")
public String URL;
#Override
public String toString()
{
return this.title;
}
public Long getId() {
return id;
}
public String getTitle()
{
return title;
}
#Relationship(type = "Sells", direction = Relationship.INCOMING)
public Seller Seller;
}
ProductController.java
package com.grada.ecommerce.Controllers;
import com.grada.ecommerce.Models.Product;
import com.grada.ecommerce.Models.Seller;
import com.grada.ecommerce.Services.ProductService;
import com.grada.ecommerce.Services.SellerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class ProductController
{
final ProductService productService;
final SellerService sellerService;
#Autowired
public ProductController(ProductService productService, SellerService sellerService)
{
this.productService = productService;
this.sellerService = sellerService;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String Index(Model model)
{
Iterable<Product> products = productService.products();
model.addAttribute("products", products);
return "product_grid";
}
#RequestMapping(value = "/product", method = RequestMethod.GET)
public String ShowProduct(#RequestParam(value = "id") Long id, Model model)
{
Product product = productService.findProductByID(id);
if(product == null)
return "redirect:/";
model.addAttribute("product", product);
return "productid";
}
#RequestMapping(value = "/add")
public String AddProduct(Model model)
{
model.addAttribute("product", new Product());
return "add";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String AddProduct(#ModelAttribute Product product)
{
productService.addProduct(product);
return "redirect:/";
}
#RequestMapping(value = "/delete", method = RequestMethod.GET)
public String DeleteProduct(Model model)
{
model.addAttribute("product", new Product());
return "delete";
}
#RequestMapping(value = "/delete", method = RequestMethod.POST)
public String DeleteProduct(#ModelAttribute Product product)
{
productService.deleteProduct(product);
return "redirect:/";
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String LoginPage(Model model)
{
return "login";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String Authenticate(Model model, String username, String password)
{
if (username.equalsIgnoreCase("seller#fake.com") && password.equals("fakeseller"))
{
Iterable<Product> products = productService.products();
model.addAttribute("products", products);
return "loggedin";
}
else
return "redirect:/login";
}
#RequestMapping(value = "/policy", method = RequestMethod.GET)
public String PolicyPage()
{
return "policies";
}
}

Welcome to SO.
Include setX methods for your variables in the Product class. Thymeleaf needs these to bind.
IMO, a great way to do this is to use Project Lombok and simply annotate your class with #Data. Then you won't even need to specify getters or setters (or your toString()) at all.
Use lower-case for your variables since by convention variables with a capital first letter refers to the class, not an instance variable.
As mentioned, use POST instead of GET in your form since you are submitting data.
Use the shorthand #GetMapping and #PostMapping to make it easier to read.

Related

Thymeleaf not loading content when I add validation in Spring Boot

Intro:
My app works in the general sense. All as I want. But when I set up validation (and it does work), it won't load all my hidden content. I moved it all to its own page, and it still did not work. So I am asking here with my original set up. I understand it is messy, but I will clean it up once I figure this out.
I can't find anything related to my problem, which makes me think, the way that I am doing it might not be the best approach. So pointers are more than welcome.
Problem:
Adding Validation to my forms breaks my Thymeleaf template. Unable to hide and unhide elements. It looks stuck. But on the terminal, the program does run fine.
My controller
(it's big... I still need to learn how to break it in smaller chunks.)
#Controller
#SessionAttributes({"guess", "word", "result", "level", "attempt", "message", "credits", "name", "gameScore"})
public class GameController {
private static final String WORD_TO_GUESS_CONSTANT = "WORD_TO_GUESS";
private static final String GUESSED_WORD_CONSTANT = "GUESSED_WORD";
private static final String RESULT_CONSTANT = "RESULT_WORD";
private static final String ATTEMPTS_CONSTANT = "ATTEMPTS";
private static final String TOTAL_ATTEMPTS_CONSTANT = "TOTAL_ATTEMPTS";
private static final String MESSAGE_CONSTANT = "MESSAGE";
private static final String CREDITS_CONSTANT = "CREDITS";
private static final String SELECTED_LEVEL_CONSTANT = "SELECTED_LEVEL";
private static final String NAME_CONSTANT = "NAME";
private static final String GAME_SCORE_CONSTANT = "GAME_SCORE";
private static final String SCORE_MULTIPLIER_CONSTANT = "SCORE_MULTIPLIER";
#Autowired
private RandomWordService randomWord;
#Autowired
private WordCheckService checkGuess;
#Autowired
private IsWordCorrectService isWordCorrectService;
#Autowired
private ScoreSavingService scoreSavingService;
#ModelAttribute("gameDto")
public GameDTO guessDTOForm() {
return new GameDTO();
}
#ModelAttribute("score")
public Score score() {
return new Score();
}
// GAME METHODS
#GetMapping(value = "/index")
public String home(Model model,
final HttpServletRequest request,
final HttpSession session,
GameDTO gameDTO,
Score score) {
model.addAttribute("name", session.getAttribute(NAME_CONSTANT));
model.addAttribute("levelSelected", session.getAttribute(SELECTED_LEVEL_CONSTANT));
model.addAttribute("attempt", session.getAttribute(ATTEMPTS_CONSTANT));
model.addAttribute("credits", session.getAttribute(CREDITS_CONSTANT));
model.addAttribute("attemptStart", session.getAttribute(TOTAL_ATTEMPTS_CONSTANT));
model.addAttribute("guess", session.getAttribute(GUESSED_WORD_CONSTANT));
model.addAttribute("result", session.getAttribute(RESULT_CONSTANT));
model.addAttribute("message", session.getAttribute(MESSAGE_CONSTANT));
model.addAttribute("gameScore", session.getAttribute(GAME_SCORE_CONSTANT));
model.addAttribute("lvlName", Level.values());
return "index";
}
#PostMapping(value = "/loadgame")
public String loadWord(
final HttpSession session, final HttpServletRequest request,
#ModelAttribute("score") Score score,
#Valid GameDTO gameDTO, BindingResult bindingResult,
Model model
) throws IOException {
if (bindingResult.hasErrors()) {
model.addAttribute("lvlName", Level.values());
model.addAttribute("name", session.getAttribute(NAME_CONSTANT));
model.addAttribute("levelSelected", session.getAttribute(SELECTED_LEVEL_CONSTANT));
model.addAttribute("attempt", session.getAttribute(ATTEMPTS_CONSTANT));
model.addAttribute("credits", session.getAttribute(CREDITS_CONSTANT));
model.addAttribute("attemptStart", session.getAttribute(TOTAL_ATTEMPTS_CONSTANT));
model.addAttribute("guess", session.getAttribute(GUESSED_WORD_CONSTANT));
model.addAttribute("result", session.getAttribute(RESULT_CONSTANT));
model.addAttribute("message", session.getAttribute(MESSAGE_CONSTANT));
model.addAttribute("gameScore", session.getAttribute(GAME_SCORE_CONSTANT));
return "index";
}
// NEW GAME
String word = (String) request.getSession().getAttribute(WORD_TO_GUESS_CONSTANT);
if (word == null) {
request.getSession().setAttribute(NAME_CONSTANT, gameDTO.getPlayerName());
request.getSession().setAttribute(ATTEMPTS_CONSTANT, gameDTO.getLvlName().getAttempts());
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, randomWord.selectRandomWord());
request.getSession().setAttribute(CREDITS_CONSTANT, gameDTO.getCredit());
request.getSession().setAttribute(SELECTED_LEVEL_CONSTANT, gameDTO.getLvlName().getLvlName());
request.getSession().setAttribute(TOTAL_ATTEMPTS_CONSTANT, gameDTO.getLvlName().getAttempts());
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameDTO.getScore());
request.getSession().setAttribute(SCORE_MULTIPLIER_CONSTANT, gameDTO.getLvlName().getMultiplier());
gameDTO.setWord((String) session.getAttribute(WORD_TO_GUESS_CONSTANT));
}
model.addAttribute("message", "");
return "redirect:/index";
}
#PostMapping(value = "/guess")
public String guessWord(
final HttpSession session,
final HttpServletRequest request,
#ModelAttribute("score") Score score,
#Valid GameDTO gameDTO, BindingResult bindingResult) throws IOException {
if (bindingResult.hasErrors()) {
return "index";
}
// variables
int attempts = (int) session.getAttribute(ATTEMPTS_CONSTANT);
int credits = (int) session.getAttribute(CREDITS_CONSTANT);
int startAttempts = (int) session.getAttribute(TOTAL_ATTEMPTS_CONSTANT);
String name = (String) session.getAttribute(NAME_CONSTANT);
// check word
String wordToGuess = (String) session.getAttribute(WORD_TO_GUESS_CONSTANT);
String guess = gameDTO.getGuess();
String result = checkGuess.resultWord(wordToGuess, guess);
String lvl = (String) session.getAttribute(SELECTED_LEVEL_CONSTANT);
// adjust score according to result
boolean wordIsCorrect = isWordCorrectService.isTheWordCorrect(result, wordToGuess);
int gameScore = (int) session.getAttribute(GAME_SCORE_CONSTANT);
int scoreMultiplier = (int) request.getSession().getAttribute(SCORE_MULTIPLIER_CONSTANT);
int wrongWord = gameDTO.getWrongWord();
int initialScore = gameDTO.getStartScore();
int finalScorePerWord = initialScore * scoreMultiplier;
// GAME LOGIC
if (!wordIsCorrect) {
String message = "";
message = "Wrong! Try again!";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, --attempts);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore - wrongWord);
log(GameController.class, "Updated score: " + session.getAttribute(GAME_SCORE_CONSTANT));
if (attempts == 0) {
request.getSession().setAttribute(CREDITS_CONSTANT, --credits);
message = "Sorry, the word was: [ " + session.getAttribute(WORD_TO_GUESS_CONSTANT) + " ]";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, startAttempts);
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, randomWord.selectRandomWord());
}
if (credits == 0) {
message = "Game over!";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore);
// SAVE SCORE
score.setGameScore(gameScore);
score.setName(name);
score.setSelectedLevelName(lvl);
scoreSavingService.saveScore(score);
log(GameController.class, "Final score: " + session.getAttribute(GAME_SCORE_CONSTANT));
}
} else {
String message = "Correct! Guess another word!";
wordToGuess = randomWord.selectRandomWord();
gameDTO.setWord(wordToGuess);
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, wordToGuess);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, startAttempts);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore + finalScorePerWord);
log(GameController.class, "Current score 2: " + session.getAttribute(GAME_SCORE_CONSTANT));
}
request.getSession().setAttribute(GUESSED_WORD_CONSTANT, guess);
request.getSession().setAttribute(RESULT_CONSTANT, result);
log(GameController.class, "Attempts are now: " + session.getAttribute(ATTEMPTS_CONSTANT));
return "redirect:/index";
}
// BUTTONS
#PostMapping(value = "/save")
public String giveUpAndSaveScore(final HttpSession session,
final HttpServletRequest request,
#ModelAttribute("score") Score score) {
score.setGameScore((Integer) session.getAttribute(GAME_SCORE_CONSTANT));
score.setName((String) session.getAttribute(NAME_CONSTANT));
score.setSelectedLevelName((String) session.getAttribute(SELECTED_LEVEL_CONSTANT));
scoreSavingService.saveScore(score);
request.getSession().invalidate();
return "index";
}
#GetMapping(value = "/scores")
public String seeScores(final HttpServletRequest request, Model model) {
List<Score> scoreList = scoreSavingService.getScore(5, 1);
model.addAttribute("score", scoreList);
return "scores";
}
// CLOSE SESSION
#PostMapping(value = "/destroy")
public String restartGame(final HttpServletRequest request) {
log(GameController.class, " Session closing. Removing the data.");
request.getSession().invalidate();
return "redirect:/index";
}
// EXCEPTION HANDLERS
#ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
public String handleArrayIndexOutOfBoundsException(final Model model) {
String text = "ERROR: Could not check empty <<guess>>.";
model.addAttribute("text", text);
return "ExceptionPage";
}
#ExceptionHandler(value = NullPointerException.class)
public String handleNullPointerException(final Model model) {
String text = "ERROR: Cannot compare words because <<word to guess>> is null";
model.addAttribute("text", text);
return "ExceptionPage";
}
}
Thymeleaf template for index.html
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html lang="en" xmlns:th="www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>5Letters</title>
<link th:href="#{/bootstrap.min.css}" rel="stylesheet" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Quicksand&display=swap" rel="stylesheet">
</head>
<body>
<div class="container-sm">
<div class="col-xs-12" align="center">
<!-- HEADER -->
<img src="https://marisabel.nl/wp-content/uploads/2020/11/avatar-e1606738130828-150x150.png"
style="margin-top:2vh;">
<h1 style="margin-top:1vh; color:hotpink;"><b>5Letter Word Game</b></h1>
<h5>AKA: Lingo.</h5>
<hr>
</div>
<form method="get" th:action="#{/scores}">
<input type="submit" value="scores" class="btn btn-primary" />
</form>
<!-- Set Up Game Form -->
<div class="col-xs-12" align="center" th:hidden="${credits} != null">
<form th:action="#{/loadgame}" th:object="${gameDTO}" method="post">
<p>
<select class="form-group" th:field="*{lvlName}" id="dropDownList">
<option th:each="lvl : ${lvlName}" th:text="${lvl.lvlName}" th:value="${lvl.lvlName}">
</option>
</select>
</p>
<p><input type="text" class="form-group mx-sm-3 mb-2" id="name" th:field="*{playerName}"
th:placeholder="name" th:value="anonymous" /></p>
<p class="alert alert-danger" th:if="${#fields.hasErrors('playerName')}" th:errors="*{playerName}">
</p>
<p><input type="submit" value="start" class="btn btn-primary" /></p>
</form>
<p></p>
</div>
<!-- This whole session will load after name and level are chosen. Credits will be set to 3, triggering them to unhide -->
<div class="row">
<div class="col-xs-12 col-md-6" align="center" th:hidden="${credits} == null">
<!-- game information : always show after game setup -->
<h3 th:text="'Hello ' + ${name} + '!'"></h3>
<h3 th:text="'Level: '+ ${levelSelected}"></h3>
<h2 th:text="'Credits : '+ ${credits} + ' | Score: '+ ${gameScore}"></h2>
<h2 th:text="${attempt} + ' / ' + ${attemptStart}"></h2>
</div>
<div class="col-md-6" align="center">
<p>
<!-- Result messages and word after guessing -->
<h4 th:text="${message}"></h4>
<h2 id="result" th:text="${result}" th:hidden="${credits} == 0"></h2>
<!-- GUESS FORM -->
<form th:action="#{/guess}" th:object="${gameDTO}" method="post" th:hidden="${credits} == null">
<input id="guessField" type="text" th:field="*{guess}" placeholder="5letters" />
<p></p>
<p class="alert alert-danger" th:if="${#fields.hasErrors('guess')}" th:errors="*{guess}"></p>
<input type="submit" value="guess" th:disabled="${credits} == 0" class="btn btn-primary" />
</form>
</p>
</div>
</div>
<div class="row" style="margin-top:10vh;">
<div class="row justify-content-center" th:hidden="${credits} == null">
<div class="col col-lg-2 align-items-center">
<!-- Destroy session data and go to index -->
<form method="post" th:action="#{/destroy}">
<input type="submit" value="play again" class="btn btn-danger" />
</form>
<p></p>
</div>
<div class="col-md-auto align-items-center" width="50%">
<!-- Display last typed word -->
<h4 th:text="'Your guess was:'" th:hidden="${attempt} == ${attemptStart}"></h4>
<h2 id="guess" th:text="${guess}" th:hidden="${attempt} == ${attemptStart}"></h2>
<p></p>
</div>
<div class="col col-lg-2 align-items-center">
<!-- Stops the game if you are bored. Usually needed with EASY mode. -->
<form method="post" th:action="#{/save}">
<input type="submit" value="i'm tired" class="btn btn-danger" />
<p></p>
</form>
</div>
</div>
</div>
</body>
</html>
I moved the content I was hiding to its own page. It loads the page, but the content remains hidden. Even after taking a break I am still unable to find what is wrong. Specially when it works 100% without validation.

Neither BindingResult nor plain target object for bean name 'cliente' available as request attribute

I'm facing an issue with spring and thymeleaf, i'm trying to fill a form with data from an entity called cliente, but i'm getting a Whitelabel Error Page message in the browser and this message in the console
Neither BindingResult nor plain target object for bean name 'cliente' available as request attribute
this is the Cliente
package com.bolsadeideasspringboot.app.models.entity;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.springframework.format.annotation.DateTimeFormat;
#Entity
#Table(name="clientes")
public class Cliente implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty
private String nombre;
#NotEmpty
private String apellido;
#NotEmpty
#Email
private String email;
#Column(name="create_at")
#Temporal(TemporalType.DATE)
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date createAt;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellido() {
return apellido;
}
public void setApellido(String apellido) {
this.apellido = apellido;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getCreateAt() {
return createAt;
}
public void setCreateAt(Date createAt) {
this.createAt = createAt;
}
}
this is the controller method
#RequestMapping(value="/form/{id}")
public String editar(#ModelAttribute("form") #PathVariable(value="id")Long id, Map<String, Object>model) {
Cliente cliente = null;
if(id > 0) {
clientedao.findOne(id);
model.put("cliente", cliente);
model.put("titulo", "Editar Cliente");
return "form";
}
else {
return "redirect:/listar";
}
}
this is the ClienteDaoImpl.java method
#Override
public Cliente findOne(Long id) {
// TODO Auto-generated method stub
return em.find(Cliente.class, id);
}
this is the Dao interface method
public Cliente findOne(Long id);
and this is the form
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title th:text="${titulo}">Insert title here</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-3">
<h1 class="text-success" th:text="${titulo}"></h1>
<form th:action="#{/form}" th:object="${cliente}" method="post">
<div class="form-group">
<label>Nombre</label>
<input class="form-control" type="text" th:field="*{nombre}" placeholder="Nombre"/>
<small class="form-text text-danger" th:if="${#fields.hasErrors('nombre')}" th:errors="*{nombre}"></small>
</div>
<div class="form-group">
<label>Apellido</label>
<input class="form-control" type="text" th:field="*{apellido}" placeholder="Apellido"/>
<small class="form-text text-danger" th:if="${#fields.hasErrors('apellido')}" th:errors="*{apellido}"></small>
</div>
<div class="form-group">
<label>Email</label>
<input class="form-control" type="text" th:field="*{email}" placeholder="correo#ejemplo.com"/>
<small class="form-text text-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></small>
</div>
<div class="form-group">
<label>Fecha</label>
<input class="form-control" type="text" th:field="*{createAt}" placeholder="DD/MM/YYYY"/>
<small class="form-text text-danger" th:if="${#fields.hasErrors('createAt')}" th:errors="*{createAt}"></small>
</div>
<div class="form-group">
<input class="btn btn-primary" type="submit" value="Crear Cliente" />
</div>
<input type="hidden" th:field="*{id}" />
</form>
</div>
</div>
</div>
</body>
</html>
i'm setting the cliente in the controller method and i'm using th:object in the form, so i don't know what i'm doing wrong, any help would be helpul, thanks in advice
Instead of put(), try using the recommended approach to adding an object to your model with
model.addAttribute("cliente", clientedao.findOne(id));
You typically want to use #GetMapping as well for your requests to populate the form. And use #PostMapping for submissions.
Aside: also take a look at Project Lombok to make your beans less error-prone and more readable. You could remove all those getters and setters and just annotate the class with #Data.
While not deprecated, you'll also want to move away from using java.util.Date and use the newer date/time classes.

Spring + Thymeleaf can't pass the parameter correctly

I can't pass the parameter correctly. I know the error was in the HTML.
The error was in the code ${vo.news.likeCount}, ${vo.news.link}, and so on.
But I don't know how to use it in the correct way. Thank you.
This is a thymeleaf template:
<div class="posts">
<div th:each="vo : ${vos}">
<div class="post">
<div class="votebar">
<button class="click-like up" aria-pressed="false" title="赞同"><i class="vote-arrow"></i>
<span class="count"><span th:text="${vo.news.likeCount}"></span></span>
</button>
<button class="click-dislike down" aria-pressed="true" title="反对"><i class="vote-arrow"></i>
</button>
</div>
<div class="content" data-url="${vo.news.link}">
<div>
<img class="content-img" src="${vo.news.image}" alt="">
</div>
</div>
</div>
</div>
</div>
This is class ViewObject:
public class ViewObject {
private Map<String, Object> obj = new HashMap<String, Object>();
public void set(String key, Object value) {
obj.put(key, value);
}
public Object get(String key) {
return obj.get(key);
}
}
This is the controller method:
#RequestMapping(path = {"/", "/index"}, method = {RequestMethod.GET, RequestMethod.POST})
public String index(Model model) {
List<News> newsList = newsService.getLatesNews(0, 0, 10);
List<ViewObject> vos = new ArrayList<>();
for (News news : newsList) {
ViewObject vo = new ViewObject();
vo.set("news", news);
vo.set("user", userService.getUser(news.getUserId()));
vos.add(vo);
}
model.addAttribute("vos", vos);
return "home.html";
}
The expression vo.news.likeCount means vo.getNews().getLikeCount(). ViewObject doesn't have method getNews(). You need to structure your expressions to look the same way they would in java.
<span th:text="${vo.get('news').likeCount}"></span>
<img class="content-img" src="${vo.get('news').image}" alt="">
<div class="content" data-url="${vo.get('news').link}">
etc...

Can I have more than 1 form action(form inside form) in single jsp using hibernate

In this jsp, I have used 2 form actions, one to save the role(table:defining role), and other to call the drop list from other table(table: solutionList).
After clicking on submit, it is not doing anything.
If i remove this form(solutionMaster.html)
Im getting this error " Invalid property 'sMName' of bean class [com.mode;.definingrole]: Bean property 'sMName' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?.
Yes, I know the reason, Because the column 'sMName' was not the part of defining role table. what to do with this.
The Thing i want to know is,
Can we create form inside form?
2.Without creating another form, How can i get the column values of other table to defining role table?
Please help.
Thanks in advance.!!!
<div class="modal inmodal" id="myModalForRole" tabindex="-1"
role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content animated bounceInRight">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">×</span><span class="sr-only">Close</span>
</button>
<i class="fa fa-laptop modal-icon"></i>
<h4 class="modal-title">Define Role</h4>
</div>
<div class="modal-body">
<form:form action="newRoleDetails.html" method="post"
commandName="deftemp" id="deftemp">
<div class="row">
<div class="form-group">
<form:form action="solutionName.html" method="post"
commandName="soltemp" id="soltemp">
<div class="col-sm-6">
<label>Solutions*</label><br>
<form:select path="sMName" class="form-control"
id="sMName">
<form:option value="" label="--select--"></form:option>
<c:forEach var="solutionList" items="${solutionList}"
varStatus="loop">
<form:option value="${solutionList}"
label="${solutionList}">
</form:option>
</c:forEach>
</form:select>
</div>
</form:form>
</div>
</div>
<div class="row">
<div class="form-group">
<div class="col-sm-6">
<label>Parent Role*</label><br>
<form:textarea path="ParentRole" id="ParentRole"
class="form-control" placeholder="Enter the Parent Role" />
</div>
</div>
</div>
<div class="row">
<div class="form-group">
<div class="col-sm-6">
<label>Sub Role*</label><br>
<form:textarea path="SubRole" id="SubRole"
class="form-control" placeholder="Enter the Child role" />
</div>
</div>
</div>
<br>
<br>
<div class="modal-footer">
<button type="button" class="btn btn-white"
data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Submit
</button>
</div>
</form:form>
</div>
Models: (solutionlist.java)
package com.model;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table (name="solutionlist")
public class solutionlist implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="solutionId",nullable = false,columnDefinition = "UNSIGNED INT(4)")
Integer solutionId;
#Column(name="solutionName")
String solutionName;
#Column(name="solutionOwner")
String solutionOwner;
#Column(name="ownerMailId")
String ownerMailId;
#Column(name="additionDate")
String additionDate;
public Integer getSolutionId() {
return solutionId;
}
public void setSolutionId(Integer solutionId) {
this.solutionId = solutionId;
}
public String getSolutionName() {
return solutionName;
}
public void setSolutionName(String solutionName) {
this.solutionName = solutionName;
}
public String getSolutionOwner() {
return solutionOwner;
}
public void setSolutionOwner(String solutionOwner) {
this.solutionOwner = solutionOwner;
}
public String getOwnerMailId() {
return ownerMailId;
}
public void setOwnerMailId(String ownerMailId) {
this.ownerMailId = ownerMailId;
}
public String getAdditionDate() {
return new SimpleDateFormat("yyyy-MM-dd HH-mm-ss.SSS")
.format(new Date());
}
public void setAdditionDate(String additionDate) {
this.additionDate = additionDate;
}
}
definingrole.java
package com.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="definingrole")
public class definingrole implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="SNo")
Integer SNo;
#Column(name="Solutions")
String Solutions;
#Column(name="ParentRole")
String ParentRole;
#Column(name="SubRole")
String SubRole;
public Integer getSNo() {
return SNo;
}
public void setSNo(Integer sNo) {
SNo = sNo;
}
public String getSolutions() {
return Solutions;
}
public void setSolutions(String solutions) {
Solutions = solutions;
}
public String getParentRole() {
return ParentRole;
}
public void setParentRole(String parentRole) {
ParentRole = parentRole;
}
public String getSubRole() {
return SubRole;
}
public void setSubRole(String subRole) {
SubRole = subRole;
}
}
Controller : newRoleDetails.html
#RequestMapping(value=NEWROLEDETAILS_PATH)
public String newRoleDetails(Map<String, Object> model, definingrole value,solutionlist sol) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
String nameOfUser=TemplateService.getEmpNameOfUser(name);
model.put("nameOfUser",nameOfUser);
String userid=loginService.getUserId();
String role=loginService.getRole();
TemplateService.newRoleDetails(value);
definingrole deftemp=new definingrole();
model.put("deftemp", deftemp);
solutionlist s = new solutionlist();
ArrayList<templateDetails> listOfTemplate=TemplateService.listTemplateDetails(role,userid);
model.put("listOfTemplate",listOfTemplate);
return TEMPLATESUMMARY;
}
controller : solutionName.html
#RequestMapping("/solutionlist.html")
public String solutionName(Map<String, Object> model,solutionlist sol) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
String nameOfUser=TemplateService.getEmpNameOfUser(name);
model.put("nameOfUser",nameOfUser);
solutionlist soltemp = new solutionlist();
model.put("soltemp", soltemp);
ArrayList<String> solutionList=TemplateService.getSolutionListForTemplate();
model.put("solutionList", solutionList);
return REDIRECT_TEMPLATESUMMARY_URL;
}
You are trying to populate list inside form under the tag of form. Which is wrong approach. You won't get the value from /solutionlist.html by your form. You have to call at least a GET if you want to use /solutionlist.html controller.
If i think So then you have to do the following
You don't need the '/solutionlist.html' controller.
Just bind the ArrayList solutionList into model in the controller method where the form page served. I mean the GET method of Controller where the given form is served.
ArrayList<String> solutionList=TemplateService.getSolutionListForTemplate();
model.put("solutionList", solutionList);
You will get that solutionList directly to your form as value. So, modify the select part of your form as follows
<select name="solutions"path="Solutions">
<c:forEach items="${solutionList}" var="solution">
<option value="${solution}">${solution}</option>
</c:forEach>
</select>

Spring form data is not received in controller with mustache

I'm using a simple Spring form with mustache. However the data is not received in Spring controller. login.getId(), login.getPass() are always received as null in controller. Any clues if something have to be fixed in template or controller?
My template and controller code as below.
<form class="form-signin" id="loginForm" action="{{{appCtxt}}}/login" method="post">
<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" name="{{id}}" id="id" class="form-control" placeholder="Email address" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" name="{{pass}}" id="pass" class="form-control" placeholder="Password" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
Controller
#Controller
public class LoginController {
private LoginService loginService;
#Autowired
public void setLoginService(LoginService loginService) {
this.loginService = loginService;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(HttpServletRequest request, Model model) {
ModelAndView result = new ModelAndView();
model.addAttribute("login", new Login());
result.addObject("resources", request.getContextPath() + "/resources");
result.addObject("appCtxt", request.getContextPath());
// return "redirect:/users";
result.setViewName("home");
return result;
}
#RequestMapping(value = "login", method = RequestMethod.POST)
public String login(Login login, HttpServletRequest request){
boolean status = loginService.verifyLogin(login.getId(), login.getPass());
if(status == true) {
return "redirect:/users";
}
else
{
return "error";
}
}
}
Instead of the name="{{pass}}" you can use name="pass" assuming you Login class contains a field with the very same name. Another thing is that you need '#ModelAttribute' annotation near the Login parameter.
For easier understanding how it works, please consider following example:
Student.java
package me.disper.model;
public class Student {
private String name;
private String surname;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
}
student.html
<!DOCTYPE html>
<html lang="en" xmlns:form="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>Create new student</title>
</head>
<body>
<form name="student" action="add" method="post">
Name: <input type="text" name="name" /><br/>
Surname: <input type="text" name="surname" /><br/>
<input type="submit" value="Save" />
</form>
</body>
</html>
MyMustacheController.java
package me.disper;
import me.disper.model.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class MyMustacheController {
#GetMapping("/student")
public ModelAndView createStudent(){
ModelAndView modelAndView = new ModelAndView("student", "student", new Student());
return modelAndView;
}
#PostMapping("/add")
public ModelAndView addStudent(#ModelAttribute Student student){
ModelAndView modelAndView = new ModelAndView("created", "student", student);
return modelAndView;
}
}
created.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Student created</title>
</head>
<body>
{{#student}}
Hello {{name}} {{surname}}
{{/student}}
</body>
</html>
Take a look at the following tutorial: A guide to forms in Spring MVC. There are some helpful hints like #ModelAttribute.

Resources