I have an customer object whose fields I'm trying to access in my view with Thymeleaf. I've used the usual syntax i.e:
<p th:text="${customer.name}"></p>
however, this does not work, it does work when I use the get() method i.e:
<p th:text="${customer.get.name}"></p>
Any idea why that is happening? I'm just getting started with Thymeleaf, so I apologize in advance if this is a dumb question.
Here's my Model:
#Id
#GeneratedValue
private int id;
#NotNull
#Size(min = 2, message="Company name length must be at least 1 character long.")
private String name;
public Customer() {}
public Customer(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
And the Controller:
#RequestMapping("")
public String index(Model model) {
model.addAttribute("title", "Home");
model.addAttribute("customers", customerDao.findAllByOrderByNameAsc());
return "index";
}
#RequestMapping(value = "", method = RequestMethod.POST)
public String processFetch(#ModelAttribute Customer customer, Model model) {
model.addAttribute("title", "Home");
model.addAttribute("customers", customerDao.findAllByOrderByNameAsc());
model.addAttribute("c", customerDao.findById(customer.getId()));
return "index";
}
and the View:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments::head"></head>
<body>
<nav th:replace="fragments::nav"></nav>
<div class="container">
<h1 class="title" th:text="${title}"></h1>
</div>
<div style="text-align: center">
<form method="post" th:object="${customers}" style="display: block;margin: 0 auto;width:300px;margin-top:5%">
<select class="form-control" name="customer">
<option style="text-align:center" th:each="customer:${customers}" th:value="${customer.id}" th:text="${customer.name}"></option>
</select>
<input class="button" style="display:block;margin:0 auto;margin-top:30px" type="submit" value="Fetch" />
</form>
</div>
<div>
<h1 th:text="${c.get().name}"></h1>
</div>
</body>
</html>
Here's my Repository class:
public interface CustomerDao extends CrudRepository<Customer, Integer> {
public Iterable<Customer> findAllByOrderByNameAsc();
}
The error I try to submit the form is:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null
I'm not sure why it cannot find property 'name'. It should be part of the object.
I appreciate any insight into this!
Related
Adding this line to my html leads to status 500
<div th:if="${#fields.hasErrors('name')}" th:errors="*{newBook.name}">Name error</div>
html itself:
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:method="POST" th:action="#{/saveBook}" th:model="${newBook}" method="post">
<label for="name">Full name of your book:</label>
<input type="text" th:field="*{newBook.name}" id="name"/>
<div th:if="${#fields.hasErrors('name')}" th:errors="*{newBook.name}">Name error</div>
<br><br>
<label>Author:</label>
<input type="text" th:field="*{newBook.author}">
<br><br>
<label>Year:</label>
<input type="text" th:field="*{newBook.year}"/>
<br><br>
<label>Pages:</label>
<input type="text" th:field="*{newBook.pages}"/>
<br><br>
<input type="submit" value="OK"/>
</form>
</body>
</html>
view n2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<h2>
Welcome to my pirate Library
<br>
<a href="allBooks" >Book list</a>
</h2>
</body>
</html>
view n3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>All books</title>
</head>
<body>
<table>
<tbody>
<tr>
<th>Name</th>
<th>Author</th>
<th>Year</th>
<th>Pages</th>
<tr th:each="book: ${allBooks}">
<td th:text="${book.getName}"></td>
<td th:text="${book.getAuthor}"></td>
<td th:text="${book.getYear}"></td>
<td th:text="${book.getPages}"></td>
</tr>
</tr></tbody>
</table>
<br>
<input type="button" value="Add New Book" onclick="window.location.href='addBook'">
</body>
</html>
Controller:
#Controller
public class BasicController {
#Autowired
BasicService basicService;
#RequestMapping("/hello")
public String TestController() {
return "test-view";
}
#GetMapping("/allBooks")
public String showAllBooks(Model model) {
model.addAttribute("allBooks", basicService.getAllBooks());
return "all-books";
}
#GetMapping("/addBook")
public String addBook(Model model) {
Book book = new Book();
model.addAttribute("newBook", book);
return "book-adding";
}
#PostMapping("/saveBook")
public String saveBook(#ModelAttribute(name = "newBook") #Validated Book book, BindingResult bindingResult) {
if (!bindingResult.hasErrors()) {
basicService.addBook(book);
System.out.println(bindingResult.hasErrors());
return "redirect:/allBooks";
}
return "book-adding";
}
}
Entity:
#Entity
#Table(name = "books", schema = "my_db")
public class Book {
public Book() {
}
public Book(String name, String author, int year, int pages) {
this.name = name;
this.author = author;
this.year = year;
this.pages = pages;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
#NotBlank(message = "ddsadas")
#NotEmpty(message = "dsadas")
#Size(min = 2, message = "Should be greater than 2 symbols")
private String name;
private String author;
private int year;
private int pages;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
}
I simply want to show validation message next to forms, but unfortunatly I've got no clue what's wrong.
I am new to springboot and thymeleaf and I'm trying to understand the form handling methods that interact with the code. Below you will find my html documents linked to a Users class. I am trying to change them to link to a customer class amongst other things, but anytime I change the th:object=${users} to match the new entity 'customerentity' it gives me the error below. What more do I have to do to get the bean(?) to register my new attribute? Any help or links to other answers is greatly appreciated.
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'customerentity' available as request attribute
Here is my entity class
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.springframework.lang.NonNull;
#Entity
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NonNull
private String name;
#NonNull
private String email;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
// standard constructors / setters / getters / toString
}
The controller class
#Controller
public class UserController {
//set up a UserRepositoty variable
#Autowired
private UserRepository userRepository;
//create an UserRepository instance - instantiation (new) is done by Spring
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
//Mapping for the /index URL when initiated through Tomcat
#RequestMapping({"/index"})
public String showUserList(Model model) {
model.addAttribute("customers", userRepository.findAll());
return "index";
}
//Mapping for the /signup URL - calls the add-user HTML, to add a user
#RequestMapping({"/signup"})
public String showSignUpForm(Users user) {
return "add-user";
}
//Mapping for the /signup URL - to add a user
#RequestMapping({"/adduser"})
public String addUser(#Validated Users users, BindingResult result, Model model) {
if (result.hasErrors()) {
return "add-user";
}
userRepository.save(users);
return "redirect:/index";
}
//Mapping for the /edit/user URL to edit a user
#GetMapping("/edit/{id}")
public String showUpdateForm(#PathVariable("id") long id, Model model) {
Users user = userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid user Id:" + id));
model.addAttribute("customer", user);
return "update-user";
}
//Mapping for the /update/id URL to update a user
#PostMapping("/update/{id}")
public String updateUser(#PathVariable("id") long id, #Validated Users user,
BindingResult result, Model model) {
if (result.hasErrors()) {
user.setId(id);
return "update-user";
}
userRepository.save(user);
return "redirect:/index";
}
//Mapping for the /delete/id URL to delete a user
#GetMapping("/delete/{id}")
public String deleteUser(#PathVariable("id") long id, Model model) {
Users user = userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid user Id:" + id));
userRepository.delete(user);
return "redirect:/index";
}
}
The html documents
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Add User</title>
</head>
<body>
<form action="#" th:action="#{/adduser}" th:object="${users}" method="post">
<label for="name">Name</label>
<input type="text" th:field="*{name}" id="name" placeholder="Name">
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
<label for="email">Email</label>
<input type="text" th:field="*{email}" id="email" placeholder="Email">
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
<input type="submit" value="Add User">
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Add User</title>
</head>
<body>
<div th:switch="${users}">
<h2 th:case="null">No users yet!</h2>
<div th:case="*">
<h2>Users</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.name}"></td>
<td th:text="${user.email}"></td>
<td><a th:href="#{/edit/{id}(id=${user.id})}">Edit</a></td>
<td><a th:href="#{/delete/{id}(id=${user.id})}">Delete</a></td>
</tr>
</tbody>
</table>
</div>
<p>Add a new user</p>
</div>
</body>
</html>
First time making a project with Spring Boot and Hibernate, and I'm getting an error I don't know how to fix. Error occurs when trying to submit form to create a new yarn object.
The relevant controllers (HomeController.java):
#Controller
public class HomeController {
#Autowired
private BrandRepository brandRepository;
#Autowired
private ColorRepository colorRepository;
#Autowired
private WeightRepository weightRepository;
#Autowired
private YarnRepository yarnRepository;
#RequestMapping("")
public String index(Model model) {
model.addAttribute("title", "My Yarns");
return "index";
}
#GetMapping("yarn/add")
public String displayAddYarnForm(Model model) {
model.addAttribute("title", "Add Yarn");
model.addAttribute(new Yarn());
model.addAttribute("colors", colorRepository.findAll());
model.addAttribute("brands", brandRepository.findAll());
model.addAttribute("weights", weightRepository.findAll());
return "yarn/add";
}
#PostMapping("yarn/add")
public String processAddYarnForm(#ModelAttribute Yarn newYarn, Error errors,
Model model,
#RequestParam (required = false) Integer brand,
#RequestParam (required = false) Integer color,
#RequestParam (required = false) Integer weight) {
model.addAttribute("newYarn", newYarn);
Optional<Brand> yarnBrand = brandRepository.findById(brand);
Optional<Color> yarnColor = colorRepository.findById(color);
Optional <Weight> yarnWeight = weightRepository.findById(weight);
yarnRepository.save(newYarn);
return "redirect:";
}
}
The form (add.html):
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org/">
<head th:replace="fragments :: head">
<meta charset="UTF-8">
</head>
<body>
<div th:replace="fragments :: page-header"></div>
<div class="container body-content">
<form method="post" style="max-width:600px;">
<div class="form-group">
<label th:for="name">Name</label>
<input class="form-control" th:field="${yarn.name}" />
</div>
<div class="form-group">
<label th:for="brand">Brand</label>
<select th:field="${yarn.brand}">
<option th:each="brand : ${brands}"
th:text="${brand.name}"
th:value="${brand.id}"></option>
</select>
<a th:href="#{'/brand/add'}">Add Brands</a>
</div>
<div class="form-group">
<label th:for="color">Color</label>
<select th:field="${yarn.color}">
<option th:each="color : ${colors}"
th:text="${color.name}"
th:value="${color.id}"></option>
</select>
<a th:href="#{'/color/add'}">Add Colors</a>
</div>
<div class="form-group">
<label th:for="weight">Weight</label>
<select th:field="${yarn.weight}">
<option th:each="weight : ${weights}"
th:text="${weight.name}"
th:value="${weight.id}"></option>
</select>
</div>
<input type="submit" value="Add Yarn" />
</form>
</div>
</body>
</html>
the Yarn model:
#Entity
public class Yarn extends AbstractEntity{
#ManyToOne
private Brand brand;
#ManyToOne
private Weight weight;
#ManyToOne
private Color color;
public Yarn() {
}
public Yarn(Brand brand, Weight weight, Color color) {
this.brand = brand;
this.weight = weight;
this.color = color;
}
public Brand getBrand() {
return brand;
}
public void setBrand(Brand brand) {
this.brand = brand;
}
public Weight getWeight() {
return weight;
}
public void setWeight(Weight weight) {
this.weight = weight;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
}
and the AbstractEntity class every model extends:
#MappedSuperclass
public abstract class AbstractEntity {
#Id
#GeneratedValue
private Integer id;
private String name;
public AbstractEntity() {
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AbstractEntity(String name) {
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractEntity that = (AbstractEntity) o;
return id == that.id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
(I have models for color, weight, brand too but these seem redundant)
And the error:
There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='yarn'. Error count: 3
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 3 errors
Field error in object 'yarn' on field 'brand': rejected value [1]; codes [typeMismatch.yarn.brand,typeMismatch.brand,typeMismatch.one.philosopherstone.knittingconversions.models.Brand,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [yarn.brand,brand]; arguments []; default message [brand]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'one.philosopherstone.knittingconversions.models.Brand' for property 'brand'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'one.philosopherstone.knittingconversions.models.Brand' for property 'brand': no matching editors or conversion strategy found]
I'm not sure why the types sent by the form don't match, any help would greatly be appreciated.
The problem seems to be with this
th:value="${brand.id}"></option> in your HTML page.
Here we are supposed to pass a brand rather than it's id, just change it as given below
and you will also have to change it in color and weight selection.
th:value="${brand}"></option>
My Controller:
#Controller
public class MainController {
#GetMapping("/")
public String mainPage() {
return "main";
}
#RequestMapping(value = "/", params = {"addRow"})
public String addRow(ModelForCheck modelForCheck, BindingResult bindingResult) {
modelForCheck.getVariables().add(new Variable());
return "main";
}
}
My model class that I used to test my code:
public class ModelForCheck {
private List<Variable> variables = new ArrayList<>();
public List<Variable> getVariables() {
return variables;
}
public void setVariables(List<Variable> variables) {
this.variables = variables;
}
}
My model class:
public class Variable {
private String name;
private String type;
public Variable() {
}
public Variable(String name, String type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
My html page (main.html):
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Hello!</title>
</head>
<body>
<form th:action="#{/}" method="post">
<h3>Variables:</h3>
<button type="submit" name="addRow">Add Row</button>
<table>
<tr th:each="variable, variableStat : *{variables}">
<td><input type="text" th:field="*{variables[__${variableStat.index}__].name}" placeholder="name"></td>
<td><input type="text" th:field="*{variables[__${variableStat.index}__].type}" placeholder="type..."></td>
</tr>
</table>
<br>
<input type="submit" value="Generate!">
</form>
</body>
</html>
enter image description here
I click on the "Add Row" button, but for some reason there is no reaction at all from the application. There are no error messages in the logs, nor any other messages. I know for sure that the "addRow" method is launched, since I checked it in the debug
I think It is doesn't work because you don't add modelForCheck to Thymeleaf model in addRow method in your controller.
I'm trying to submit form data from thymeleaf where the relationship between the class is one to many. The form has multiple fields with same properties so I'm using array to submit the form. I'm getting field not found exception like this. How do we set the property between the classes that has one to many relationship?
org.springframework.beans.NotReadablePropertyException: Invalid property 'education.name[0]' of bean class [pro.budthapa.domain.Resume]: Bean property 'education.name[0]' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
index.html
<form th:action="#{/resume/new}" th:method="post" th:object="${resume}" class="form-horizontal" enctype="multipart/form-data">
<div class="form-group">
<label class="control-label col-sm-3" for="college">College/University Name:</label>
<div class="col-sm-6">
<input type="text" class="form-control" th:field="*{education.name[0]}" placeholder="college /university" />
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3 col-sm-offset-2" for="college">Course:</label>
<div class="col-sm-6">
<input type="text" class="form-control" th:field="*{education.course[0]}" placeholder="course of study" />
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3" for="college">College/University Name:</label>
<div class="col-sm-6">
<input type="text" class="form-control" th:field="*{education.name[1]}" placeholder="college /university" />
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3 col-sm-offset-2" for="college">Course:</label>
<div class="col-sm-6">
<input type="text" class="form-control" th:field="*{education.course[1]}" placeholder="course of study" />
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
Entity class
Education.class
#Entity
public class Education {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="college_name")
private String name;
#Column(name="course_name")
private String course;
#ManyToOne
#JoinColumn(name="resume_id")
private Resume resume;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
public Resume getResume() {
return resume;
}
public void setResume(Resume resume) {
this.resume = resume;
}
}
Resume.class
#Entity
public class Resume {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy="resume")
private Set<Education> education;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Education> getEducation() {
return education;
}
public void setEducation(Set<Education> education) {
this.education = education;
}
}
Controller
ResumeController.class
#Controller
public class ResumeController {
private static final String ADD_RESUME="resume/addResume";
#Autowired
private ResumeService resumeService;
#GetMapping("/resume/new")
public String addResume(Resume resume, Model model){
model.addAttribute("resume",resume);
return ADD_RESUME;
}
#PostMapping("/resume/new")
public String addResume(#Valid Resume resume, BindingResult result, Model model){
resumeService.save(resume);
model.addAttribute("resume",resume);
return ADD_RESUME;
}
}
You got the property navigation slightly wrong, change to this (and similar for the other fields):
<input type="text" class="form-control" th:field="*{education[0].name}" placeholder="college /university" />
Then if a remember correctly you must use List instead of Set:
private List<Education> education;
public List<Education> getEducation() {
return education;
}
public void setEducation(List<Education> education) {
this.education = education;
}
Because a Set has no indexes.