The scenario :
Java + springboot + thymeleaf. I have a question entity which has a #OneToMany relationship with an answer entity :
#Entity
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_gen")
#SequenceGenerator(name = "question_gen", sequenceName = "QUESTION_SEQ", allocationSize = 1)
private long questionId;
#Column
private String questionText;
#OneToMany(cascade = CascadeType.PERSIST)
#LazyCollection(LazyCollectionOption.FALSE)
private List<Answer> answers = new ArrayList<>();
#Column
private String correctAnswer;
#Column
private boolean approved;
#ManyToMany(cascade = CascadeType.PERSIST)
#LazyCollection(LazyCollectionOption.FALSE)
private List<Tag> tags;
public Question(String questionText, List<Answer> answers, String correctAnswer, boolean approved) {
super();
this.questionText = questionText;
this.answers = answers;
this.correctAnswer = correctAnswer;
this.approved = approved;
}
public Question(String questionText, List<Answer> answers, String correctAnswer, boolean approved, List<Tag> tags) {
super();
this.questionText = questionText;
this.answers = answers;
this.correctAnswer = correctAnswer;
this.approved = approved;
this.tags = tags;
}
public Question() {
super();
}
standard getter and setter etc follow
#Entity
public class Answer {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "answer_gen")
#SequenceGenerator(name = "answer_gen", sequenceName = "ANSWER_SEQ", allocationSize = 1)
private long answerId;
#Column
private String answerText;
public Answer(String answerText) {
super();
this.answerText = answerText;
}
public Answer() {
super();
}
The controller
#GetMapping("/EditQuestion/{questionId}")
public ModelAndView editQuestion(#PathVariable("questionId") long questionId, ModelAndView modelAndView) {
Optional<Question> question = questionService.retrieveOne(questionId);
modelAndView.addObject("question", question.get());
modelAndView.addObject("allTags", tagService.retrieveAll());
modelAndView.addObject("answers", question.get().getAnswers());
if (question.isPresent()) {
modelAndView.setViewName("editQuestion");
return modelAndView;
}
return modelAndView;
}
#PostMapping("/EditQuestionSubmit")
public ModelAndView editQuestionSubmit(ModelAndView modelAndView, Question question) {
if (questionService.update(question)) {
modelAndView.addObject("allQuestions", questionService.retrieveAll());
modelAndView.setViewName("allQuestions");
return modelAndView;
}
modelAndView.setViewName("allQuestions");
return modelAndView;
}
listing, retrieving one and adding through a thymeleaf all working but when I try to edit the list just gets nulls
The thymeleaf :
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Edit Question Details</title>
</head>
<body>
<h1>Edit Question Details</h1>
<form action="#" th:action="#{/EditQuestionSubmit}"
th:object="${question}" method="post">
<div>
<p>
<label th:if="${questionText} == null"
th:text=" ' Edit your question'" /></label>
</p>
<p>
<input type="text" th:field="*{questionText}" />
</p>
</div>
<div>
<p>
<label th:text=" 'enter answers A-D for this question ' "></label>
</p>
<p>
<input type="text" th:field="*{answers}" value="${answerId}" />
</p>
</div>
<input type="text" th:each="answer : ${answers}" th:id="answerId" th:text="${answer.answerText}"
th:value="${answer.answerId}" name="answers" th:field="*{answers}" />
</div>
<div>
<p>
<label th:if="${correctAnswer} == null"
th:text="'Select the correct answer for this question'" /></label>
</p>
<p>
<select id="correctAnswer" name="correctAnswer"
th:value="${question.correctAnswer}"
th:selected="${question.correctAnswer}">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
</select>
</p>
</div>
<div>
<p>
<label>select relevant tags</label>
<p>
<select id="tagId" multiple name="tags"
th:selected="${question.tags}">
<option th:each="tag : ${allTags}" th:value="${tag.tagId}"
th:text="${tag.name}"></option>
</select>
</p>
</div>
<div>
<p>
<button type="submit">Edit Question</button>
</p>
</div>
<input type="hidden" th:field="*{questionId}" value="${questionId}" />
</form>
</body>
</html>
all other fields are working, I've tried a lot of different things but no joy, and input greatly appreciated
the solution is multi part. First I added a wrapper class
public class AnswerWrapper {
private List<Answer> answers;
public void addAnswer(Answer answer, Question question) {
int index = (int) question.getQuestionId();
answers = question.getAnswers();
this.answers.add(index, answer);
}
}
Then I updated the controller
#GetMapping("/EditQuestion/{questionId}")
public ModelAndView editQuestion(#PathVariable("questionId") long questionId, ModelAndView modelAndView) {
Optional<Question> question = questionService.retrieveOne(questionId);
List<Answer> answers = question.get().getAnswers();
AnswerWrapper answerWrapper = new AnswerWrapper();
for (int i = 0 ; i >= answers.size(); i++) {
Answer answer = answers.get(i);
answer.setAnswerId(answer.getAnswerId());
answerWrapper.addAnswer(answer, question.get());
}
modelAndView.addObject("question", question.get());
modelAndView.addObject("allTags", tagService.retrieveAll());
modelAndView.addObject("answers", question.get().getAnswers());
if (question.isPresent()) {
modelAndView.setViewName("editQuestion");
return modelAndView;
}
return modelAndView;
}
no changes needed for the PostMapping
Then I updated the thymeleaf
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Edit Question Details</title>
</head>
<body>
<h1>Edit Question Details</h1>
<form action="#" th:action="#{/EditQuestionSubmit}"
th:object="${question}" method="post">
<div>
<p>
<label th:if="${questionText} == null"
th:text=" ' Edit your question'" /></label>
</p>
<p>
<input type="text" th:field="*{questionText}" />
</p>
</div>
<div>
<p>
<label th:text=" 'enter answers A-D for this question ' "></label>
</p>
<div th:each="answer, answerIndex: *{answers}">
<td><input
th:field="*{answers[__${answerIndex.index}__].answerText}"
th:value="${answer.getAnswerId()}" /></td> <input hidden
th:name="|answers[${answerIndex.index}].answerId|"
th:value="${answer.getAnswerId()}" />
</div>
<div>
<p>
<label th:if="${correctAnswer} == null"
th:text="'Select the correct answer for this question'" /></label>
</p>
<p>
<select id="correctAnswer" name="correctAnswer"
th:value="${question.correctAnswer}"
th:selected="${question.correctAnswer}">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
</select>
</p>
</div>
<div>
<p>
<label>select relevant tags</label>
<p>
<select id="tagId" multiple name="tags"
th:selected="${question.tags}">
<option th:each="tag : ${allTags}" th:value="${tag.tagId}"
th:text="${tag.name}"></option>
</select>
</p>
</div>
<div>
<p>
<button type="submit">Edit Question</button>
</p>
</div>
<input type="hidden" th:field="*{questionId}" value="${questionId}" />
</form>
</body>
</html>
and now all is well
Related
I have a spring boot app and i need to Primit multi urls in security Configration but that not working I did this :
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/login/**").antMatchers("/register").antMatchers("/home");
}
and this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/login/**").permitAll()
.antMatchers("/register").permitAll()
.antMatchers("/home").permitAll()
.and().authorizeRequests().anyRequest().authenticated().and().httpBasic();
http.exceptionHandling()
.authenticationEntryPoint(
(request, response, ex) -> {
response.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
ex.getMessage()
);
}
);
http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
but it's the same, no pages loaded - only login screen appears-
In first code :
In second one :
This is my view controller class :
#Controller
public class ViewController {
#Autowired
IEmployeeRepository employeeRepository;
#GetMapping("/home")
public String viewHomePage(){
return "index";
}
#GetMapping("/register")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new Employee());
return "signup_form";
}
#GetMapping("/login")
public String showLoginForm() {
return "login_form";
}
#PostMapping("/process_register")
public String processRegister(Employee user) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
employeeRepository.save(user);
return "register_success";
}
#GetMapping("/addCustomer")
public String addCustomer() {
return "addcustomer";
}
#GetMapping("/addItem")
public String addItem() {
return "additem";
}
}
and this is my html :
signup_ form :
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Sign Up</title>
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container text-center">
<div>
<h1>User Registration - Sign Up</h1>
</div>
<form th:action="#{/process_register}" th:object="${user}"
method="post" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="email" th:field="*{email}" class="form-control" required />
</div>
</div>
<label class="col-4 col-form-label">Serial Number : </label>
<div class="col-8">
<input type="email" th:field="*{serialNumber}" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" class="form-control"
required minlength="4" maxlength="10"/>
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">First Name: </label>
<div class="col-8">
<input type="text" th:field="*{firstName}" class="form-control"
required minlength="2" maxlength="20"/>
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Last Name: </label>
<div class="col-8">
<input type="text" th:field="*{lastName}" class="form-control"
required minlength="2" maxlength="20" />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Country: </label>
<div class="col-8">
<input type="text" th:field="*{country}" class="form-control"
required minlength="2" maxlength="20" />
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Sign Up</button>
</div>
</form>
</div>
</body>
</html>
The index html :
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Welcome</title>
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container text-center">
<h1>Welcome</h1>
<h3><a th:href="/#{/users}">List of Users</a></h3>
<h3><a th:href="/#{/register}">Register</a></h3>
<h3><a th:href="/#{/login}">Login</a></h3>
</div>
</body>
</html>
and this is Employee model :
#Entity
#Table(name = "employee")
public class Employee implements UserDetails {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "role_id")
private Role role;
private String email;
private String mobileNumber;
private String country;
private String password;
private boolean isDeleted;
#OneToMany
private Set<Invoice> invoices;
#OneToMany
private Set<InvoiceHistory> invoiceHistories;
public Employee(){}
public Employee(long serialNumber, String firstName, String lastName, Role role, String email, String mobileNumber,String country,String password){
super();
this.serialNumber = serialNumber;
this.firstName = firstName;
this.lastName = lastName;
this.role= role;
this.email = email;
this.mobileNumber = mobileNumber;
this.country = country;
this.password = password;
this.isDeleted = false;
}
The first one you are using ignoring lol.
The second one it's not Spring Security problem. This is Thymeleaf error, check your thymeleaf parsing.
I am creating a Spring Boot application with thymeleaf where I have an relationship Many-to-One between a table named figures and a table named states (many figures to one state). Everything is OK when creating a state. The problem is with creating a figure.
These are the classes:
#Entity
#Table(name = "state")
public class State implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_state")
private Integer idState;
#Column(name = "official_state_name")
private String officialStateName;
#Column(name = "official_language")
private String officialLanguage;
#Column(name = "state_currency")
private String stateCurrency;
//setters and getters
#Entity
#Table(name = "figure")
public class Figure implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_figure")
private Long idFigure;
#Column(name = "pib")
private String fullName;
#Column(name = "date_of_birth")
private java.sql.Date dateOfBirth;
#Column(name = "date_of_death", nullable = true)
private java.sql.Date dateOfDeath;
#Column(name = "kind_of_activity")
private String kindOfActivity;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "id_state", nullable = false)
private State state;
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public Long getIdFigure() {
return this.idFigure;
}
public void setIdFigure(Long idFigure) {
this.idFigure = idFigure;
}
public String getFullName() {
return this.fullName;
}
public void setFullName(String pib) {
this.fullName = fullName;
}
public java.sql.Date getDateOfBirth() {
return this.dateOfBirth;
}
public void setDateOfBirth(java.sql.Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public java.sql.Date getDateOfDeath() {
return this.dateOfDeath;
}
public void setDateOfDeath(java.sql.Date dateOfDeath) {
this.dateOfDeath = dateOfDeath;
}
public String getKindOfActivity() {
return this.kindOfActivity;
}
public void setKindOfActivity(String kindOfActivity) {
this.kindOfActivity = kindOfActivity;
}
}
Now this is the controller for the Figure class:
#Controller
public class FigureController {
#Autowired
FigureService figureService;
#Autowired
FigureRepository figureRepository;
#Autowired
StateRepository stateRepository;
#Autowired
StateService stateService;
#GetMapping("/figures")
public String showAllFigures(Model model){
List<Figure> listFigures = figureService.findAllFigures();
model.addAttribute("listFigures", listFigures);
return "figures";
}
#GetMapping("/figures/new")
public String showNewFigureForm(Model model){
List <State> listStates = stateService.findAll();
model.addAttribute("listStates", listStates);
model.addAttribute("figure", new Figure());
model.addAttribute("pageTitleF", "Add New Figure");
return "figure_form";
}
#PostMapping("/figures/save")
public String saveFigure (Figure requestFigure, RedirectAttributes redirectAttributes) throws StateNotFoundException {
figureRepository.save(requestFigure);
redirectAttributes.addFlashAttribute("messageF", "The figure has been saved successfully!");
return "redirect:/figures";
}
#GetMapping("/figures/edit/{id}")
public String showEditFigureForm(#PathVariable("id") Integer id, Model model, RedirectAttributes redirectAttributes){
try {
Figure figure = figureService.findFigureById(id);
List <State> listStates = stateService.findAll();
model.addAttribute("listStates", listStates);
model.addAttribute("figure", figure);
model.addAttribute("pageTitleF", "Edit Figure (ID: " + id + ")");
return "figure_form";
} catch (EntityNotFoundException e) {
redirectAttributes.addFlashAttribute("messageF", e.getMessage());
return "redirect:/figures";
}
}
#GetMapping("/figures/delete/{id}")
public String deleteFigure(#PathVariable("id") Integer id, Model model, RedirectAttributes redirectAttributes){
try {
figureService.deleteFigure(id);
redirectAttributes.addFlashAttribute("messageF", "The figure ID " + id + " has been deleted!");
} catch (StateNotFoundException e) {
redirectAttributes.addFlashAttribute("messageF", e.getMessage());
}
return "redirect:/figures";
}
}
So I need to pass an id of state from drop-down list of states and assign it to the foreign key of the figure entity. These are the html files.
figures.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Welcome to My Application</title>
<link rel="stylesheet" type="text/css" th:href="#{/webjars/bootstrap/css/bootstrap.min.css}"/>
</head>
<body>
<div class="container-fluid text-center">
<div><h2>Manage Figures</h2></div>
<div class="m-2">
<a class="h3" th:href="#{/figures/new}">Add New Figure</a>
</div>
<div class="m-2">
<a class="h3" th:href="#{http://localhost:8080/}">Back to Main Menu</a>
</div>
<div th:if="${messageF}" class="alert alert-success">
[[${messageF}]]
</div>
<div>
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Full Name</th>
<th>Date of Birth</th>
<th>Date of Death</th>
<th>kindOfActivity</th>
<th></th>
</tr>
</thead>
<tbody>
<th:block th:each="figure : ${listFigures}">
<tr>
<td>[[${figure.fullName}]]</td>
<td>[[${figure.dateOfBirth}]]</td>
<td>[[${figure.dateOfDeath}]]</td>
<td>[[${figure.kindOfActivity}]]</td>
<td>
<a class="h4 mr-3" th:href="#{'/figures/edit/' +${figure.idFigure}}">Edit</a>
<a class="h4" th:href="#{'/figures/delete/' +${figure.idFigure}}">Delete</a>
</td>
</tr>
</th:block>
</tbody>
</table>
</div>
</div>
</body>
</html>
figure_form.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>[[${pageTitleF}]]</title>
<link rel="stylesheet" type="text/css" th:href="#{/webjars/bootstrap/css/bootstrap.min.css}"/>
</head>
<body>
<div class="container-fluid">
<div class="text-center"><h2>[[${pageTitleF}]]</h2></div>
<form th:action="#{/figures/save}" method="post" th:object="${figure}"
style="max-width: 500px; margin: 0 auto;">
<input type="hidden" th:field="*{idFigure}">
<div class="border border-secondary rounded p-3">
<div class="form-group row">
<label class="col-sm-4 col-form-label">Full Name</label>
<div class="col-sm-8">
<input type="text" th:field="*{fullName}" class="form-control" required minlength="4" maxlength="40"/>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Date of Birth</label>
<div class="col-sm-8">
<input type="date" th:field="*{dateOfBirth}" class="form-control" />
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Date of Death</label>
<div class="col-sm-8">
<input type="date" th:field="*{dateOfDeath}" class="form-control" />
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Kind of Activity</label>
<div class="col-sm-8">
<input type="text" th:field="*{kindOfActivity}" class="form-control" required maxlength="60"/>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">State</label>
<div class="col-sm-8">
<select class="form-control" id="idState" name="idState">
<option value="">Select State</option>
<option th:each="state : ${listStates}"
th:value="${state.idState}"
th:text="${state.idState}+' : '+${state.officialStateName}"></option>
</select>
</div>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary m-2">Save</button>
<button type="button" class="btn btn-secondary m-2" onclick="cancelForm()">Cancel</button>
</div>
</div>
</form>
</div>
<script type="text/javascript">
function cancelForm(){
window.location = "[[#{/figures}]]"
}
</script>
</body>
</html>
So the form looks like this:
When I hit the save button I get an error status 500:
And the error description
org.hibernate.PropertyValueException: not-null property references a null or transient value : com.historicalreferencebook.historicalreferencebook.domain.Figure.state
As you can see even though I chose a state and therefore its key is assigned to the foreign key of the figure entity, it is null. Why? And what should I do to pass the value from the drop down list to the foreign key of the figure? Any help is appreciated! Thanks in advance!
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "id_state", nullable = false)
private State state;
change to:
#ManyToOne(fetch = FetchType.LAZY,
optional = false,
cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "id_state", nullable = false)
private State state;
I got it solved by dropping a table and then creating it again with Maven installation, I changed the id data type to Integer and used this as a drop down list (because there is a "th:field" which tells Spring which field will be filled with a value from a drop down list):
<div class="form-group row">
<label class="col-sm-4 col-form-label">State</label>
<div class="col-sm-8">
<select th:field="*{state}" class="form-control" required>
<th:block th:each="state : ${listStates}">
<option th:text="${state.officialStateName}" th:value="${state.idState}"/>
</th:block>
</select>
</div>
</div>
My resister page works fine, it takes the input and pushes to data base when "register" is clicked. However, when I click to go to a "list an item" page, which similarly takes input to be pushed to a table in a database when "list item" is clicked, it comes up with "Request Method 'POST' not supported". So the "list an item" page doesn't appear when clicked from the main logged-in menu page.
Part of the controller class here:
#GetMapping("/register")
public String goToRegisterPage(Model model) {
User user = new User();
model.addAttribute("user", user);
return "register_page";
}
#GetMapping("/go_to_create_item_page")
public String goToCreateItemPage(Model model) {
FreeItem freeItem = new FreeItem();
model.addAttribute("freeItem", freeItem);
return "create_item_page";
}
Part of the html file here that offers to go to the create_item_page:
<div>
<form th:action="#{/update_user_page}" method="post">
<input type="submit" value="Update account" />
</form>
</div>
<div>
<form th:action="#{/go_to_create_item_page}" method="post">
<input type="submit" value="List an Item" />
</form>
</div>
The create_item_page:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Create Item</title>
<link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css" />
</head>
<body>
<div class="container text-center">
<div>
<h1>Create an Item to list publicly</h1>
</div>
<form th:action="#{/process_created_item}" method="post"
style="max-width: 600px; margin: 0 auto;"
th:object="${freeItem}">
<div class="m-3">
<div class="form-group row">
<label class="col-form-label col-4">Item Name</label>
<div class="col-8">
<input type="text" class="form-control" th:field="*{itemName}" required
minlength="2" maxlength="20" />
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-4">Item Description</label>
<div class="col-8">
<input type="text" class="form-control"
th:field="*{itemDescription}" required
minlength="6" maxlength="10" />
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-4">Quantity Of Item</label>
<div class="col-8">
<input type="text" class="form-control" th:field="*{quantityOfItem}" required
minlength="2" maxlength="20" />
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-4">General Item Location</label>
<div class="col-8">
<input type="text" class="form-control" th:field="*{itemLocation}"
required minlength="2" maxlength="20" />
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-4">E-mail for contact</label>
<div class="col-8">
<input type="email" class="form-control" th:field="*{email}"
required />
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">List Item</button>
</div>
</div>
</form>
</div>
</body>
</html>
And the FreeItem class that specifies the objects being pushed to the database.
package com.marinelli.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name= "free_items")
public class FreeItem {
public FreeItem(String itemName, String itemDescription, String quantityOfItem, String itemLocation, String email) {
super();
this.itemName = itemName;
this.itemDescription = itemDescription;
this.quantityOfItem = quantityOfItem;
this.itemLocation = itemLocation;
this.email = email;
}
public FreeItem() {
}
#Id
#Column(nullable = false, unique = true, length = 64)
private String itemName;
#Column(nullable = false, length = 64)
private String itemDescription;
#Column(nullable = false, length = 25)
private String quantityOfItem;
#Column(nullable = false, length = 25)
private String itemLocation;
#Column(nullable = false, unique = true, length = 45)
private String email;
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getItemDescription() {
return itemDescription;
}
public void setItemDescription(String itemDescription) {
this.itemDescription = itemDescription;
}
public String getQuantityOfItem() {
return quantityOfItem;
}
public void setQuantityOfItem(String quantityOfItem) {
this.quantityOfItem = quantityOfItem;
}
public String getItemLocation() {
return itemLocation;
}
public void setItemLocation(String itemLocation) {
this.itemLocation = itemLocation;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Override
public String toString() {
return "FreeItem [itemName=" + itemName + ", itemDescription=" + itemDescription + ", quantityOfItem="
+ quantityOfItem + ", itemLocation=" + itemLocation + ", email=" + email + "]";
}
}
you need to create go_to_create_item_page and update_user_page as #Postmapping("....")
#Postmapping("/update_user_page")
public String goToUpdateUserPage(Model model) {
...
...
...
}
#Postmapping("/go_to_create_item_page")
public String goToCreateItemPage(Model model) {
...
...
...
}
The error clearly stated that you are using the incorrect method for that provided endpoint.
In your case, /go_to_create_item_page defined as GET, your form declared method = post. Either changing them to both use POST or GET should resolve the problem (In terms of API design, it should be GET, as you are getting resources from the server rather than creating / updating).
I am creating an Spring Boot App, in which the user can create licences. The user can type the name, purchaseDate, renewalDate and expirationDate.
My problem is, that when I try to save the data, it returns null for the name, the purchaseDate, renewalDate and expirationDate.
I absolutely don't know what to do anymore, please help. The "// Comment" values in the Model:Licence are those who get null when executing the post.
Model: Licence
#Entity
#Table(name = "TBL_LICENCES")
public class Licence {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private String licenceName; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String purchaseDate; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String renewalDate; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String expirationDate; // --> null after the post
public Licence() {}
public Licence(Long id, String licenceName, String purchaseDate, String renewalDate, String expirationDate) {
Id = id;
this.licenceName = licenceName;
this.purchaseDate = purchaseDate;
this.renewalDate = renewalDate;
this.expirationDate = expirationDate;
}
// Getter and Setter
}
Model: LicenceRepository
#Repository
public interface LicenceRepository extends CrudRepository<Licence, Long> { }
Service: LicenceService
#Service
public class LicenceService {
#Autowired
private LicenceRepository licenceRepository;
public List<Licence> getLicences() {
return (List<Licence>) licenceRepository.findAll();
}
public Optional<Licence> getLicenceById(Long id) {
return licenceRepository.findById(id);
}
public void addLicence(Licence licence) {
licenceRepository.save(licence);
}
public void updateLicence(Licence licence) {
licenceRepository.save(licence);
}
public void deleteLicenceById(Long id) {
licenceRepository.deleteById(id);
}
}
Controller: LicenceController
#Controller
public class LicenceController {
#Autowired
private LicenceService licenceService;
#GetMapping("/licences")
public String getLicences(Model model) {
model.addAttribute("licences", licenceService.getLicences());
return "licences";
}
#GetMapping("/onelicence")
#ResponseBody
public Optional<Licence> getLicenceByID(Long id, Model model) {
model.addAttribute("onelicence", licenceService.getLicenceById(id));
return licenceService.getLicenceById(id);
}
#RequestMapping(value="/save", method = {RequestMethod.POST, RequestMethod.PUT, RequestMethod.GET})
public String updateLicence(Licence licence) {
licenceService.updateLicence(licence);
return "redirect:/licences";
}
**// Is performed, when clicking "New Licence > Save"**
#RequestMapping(value="/addNew", method = {RequestMethod.POST, RequestMethod.PUT, RequestMethod.GET})
public String addLicence(Licence licence) {
licenceService.addLicence(licence);
return "redirect:/licences";
}
#RequestMapping(value="/delete", method = {RequestMethod.DELETE, RequestMethod.PUT, RequestMethod.GET})
public String deleteLicence(Long id) {
licenceService.deleteLicenceById(id);
return "redirect:/licences";
}
}
licences.html
<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/html"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<script type ="text/javascript" src="webjars/jquery/3.4.1/jquery.min.js"></script>
<script type ="text/javascript" src="webjars/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<link href="webjars/bootstrap/4.4.1/css/bootstrap.css" rel="stylesheet"/>
<title>Licence</title>
</head>
<body>
<div class = "container">
<h2>Licences</h2>
<table class = "table table-striped">
<thead>
<tr>
<td>ID</td>
<td>Name</td>
<td>Kaufdatum</td>
<td>Erneuerungsdatum:</td>
<td>Auslaufdatum:</td>
</tr>
</thead>
<tbody>
<tr th:each = "licence: ${licences}">
<td th:text="${licence.id}">ID</td>
<td th:text="${licence.licenceName}">Name</td>
<td th:text="${licence.purchaseDate}">Kaufdatum</td>
<td th:text="${licence.renewalDate}">Erneuerungsdatum</td>
<td th:text="${licence.expirationDate}">Auslaufdatum</td>
<td><a class="btn btn-warning">Edit</a></td>
<td><a class="btn btn-danger">Delete</a></td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addModal" data-whatever="#mdo">New Licence</button>
</div>
// Bootstrap Varying Modal Content
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<form th:action="#{/addNew}" method ="post">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">New Licence</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="name" class="col-form-label">Lizenz Name</label>
<input type="text" class="form-control" id="licenceName" name="name">
</div>
<div class="form-group">
<label for="purchase" class="col-form-label">purchaseDate</label>
<input type="date" class="form-control" id="purchaseDate" name="purchase">
</div>
<div class="form-group">
<label for="renewalAdd" class="col-form-label">renewalDate</label>
<input type="date" class="form-control" id="renewalAdd" name="renewal">
</div>
<div class="form-group">
<label for="expirationAdd" class="col-form-label">expirationDate</label>
<input type="date" class="form-control" id="expirationAdd" name="expiration">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</form>
</div>
</body>
</html>
schema.sql
DROP TABLE IF EXISTS TBL_LICENCES;
CREATE TABLE TBL_LICENCES (
id INT AUTO_INCREMENT PRIMARY KEY,
licence_name VARCHAR(250),
purchase_date VARCHAR(250),
renewal_date VARCHAR(250),
expiration_date VARCHAR(250)
);
data.sql --> This works and the data is shown.
INSERT INTO
TBL_LICENCES (licence_name, purchase_date, renewal_date, expiration_date)
VALUES
('Test1', '2020-01-31', '2020-06-31', '2020-12-31'),
('Test', '2021-01-31', '2021-06-31', '2021-12-31');
Properties: application.properties
spring.h2.console.enabled=true
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:<dbLicences>
spring.jpa.hibernate.ddl-auto=update
The name attribute of the input control should match the name of the fields in the Licence class. Currently your id attribute is matching the name of the fields, but when a form is submitted it uses the name attribute to build the request parameters.
Update your HTML to match something like:
<input type="text" class="form-control" id="licenceName" name="licenceName" />
Fix the name for other fields and you will have the Licence object populated with data from the form.
Also, I see that
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String licenceName;
licenceName is annotated with #DateTimeFormat. I guess this is a mistake, please correct that as well.
I'm using Spring and I would like to submit my agentForm with POST method but submitNewAgent isn't called on form submit. However, it works when I replace POST by GET. I've been working on it for a few days now, and I have no idea what to change. Does anyone can help me please ?
Here my files
new.html
<!DOCTYPE html>
<html lang="fr"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorator="index" >
<head th:replace="index :: head">
<link href="../../static/css/bootstrap.min.css" rel="stylesheet" media="screen" />
</head>
<body>
<th:block layout:fragment="body">
<h1 class="page-header"> Nouvel Agent </h1>
<form class="form" th:modelAttribute="agentForm" th:object="${agentForm}" action="/agents/ajouter-submit/" method="POST">
<div class="form-group">
<label>Prénom</label>
<div th:if="${#fields.hasErrors('prenom')}" th:errors="*{prenom}" class="text-danger">
Erreur prénom
</div>
<input type="text" th:field="*{prenom}" class="form-control" />
</div>
<div class="form-group">
<label th:for="*{nom}">Nom</label>
<div th:if="${#fields.hasErrors('nom')}" th:errors="*{nom}" class="text-danger">Erreur nom</div>
<input type="text" th:field="*{nom}" class="form-control" />
</div>
<div class="form-group">
<button class="btn btn-primary"><span class="glyphicon glyphicon-ok"></span>
<span th:remove="tag" th:text="#{label.add}"></span>
</button>
</div>
</form>
</th:block>
</body>
</html>
AgentForm.class
public class AgentForm {
#NotNull
#Size(min=2, max=255)
private String prenom;
#NotNull
#Size(min=2, max=255)
private String nom;
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
#Override
public String toString() {
return "Agent{" +
"prenom='" + prenom + '\'' +
", nom='" + nom + '\'' +
'}';
}
}
AgentController.class link agent views and agent model.
#Controller
#RequestMapping("/agents")
public class AgentController {
private final AgentService agentService;
#Autowired
public AgentController(AgentService agentService) {
this.agentService = agentService;
}
/**
* Gets all agents
*
* #param model view
* #return template name
*/
#RequestMapping(value = {"/", "lister"}, method = RequestMethod.GET)
public String allAgents(Model model) {
List<Agent> agentList = agentService.findAll();
if (agentList != null)
model.addAttribute("agentList", agentList);
return "agents/list";
}
/**
* Displays form to add an agent
*
* #return template and attributes
*/
#RequestMapping(value = {"/", "ajouter"}, method = RequestMethod.GET)
public ModelAndView addAgentForm() {
AgentForm a = new AgentForm();
a.setNom("test");
a.setPrenom("prenom");
return new ModelAndView("agents/new", "agentForm", a);
}
/**
* Manages the form to add an agent and submit in the repository
*
* #param agentForm form
* #param result results
* #param model form the view
* #param attributes view attributes
* #return url
*/
#RequestMapping(value = {"/", "ajouter-submit"}, method = RequestMethod.POST)
public String submitNewAgent(#ModelAttribute("agentForm") #Validated AgentForm agentForm,
BindingResult result, Model model, final RedirectAttributes attributes) {
if (result.hasErrors())
return "agents/new";
agentService.saveAndFlush(AgentAdapter.adaptAgentFormToAgent(agentForm));
attributes.addFlashAttribute("css", "success");
attributes.addFlashAttribute("msg", "L'agent est correctement ajouté !");
return "redirect:/agents/lister";
}
}
I'm sorry for mixing English and French.
<!DOCTYPE html>
<html lang="fr"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorator="index" >
<head th:replace="index :: head">
<link href="../../static/css/bootstrap.min.css" rel="stylesheet" media="screen" />
</head>
<body>
<th:block layout:fragment="body">
<h1 class="page-header"> Nouvel Agent </h1>
<form class="form" th:modelAttribute="agentForm" th:object="${agentForm}" th:action="#{ajouter-submit}" method="POST">
<div class="form-group">
<label>Prénom</label>
<div th:if="${#fields.hasErrors('prenom')}" th:errors="*{prenom}" class="text-danger">
Erreur prénom
</div>
<input type="text" th:field="*{prenom}" class="form-control" />
</div>
<div class="form-group">
<label th:for="*{nom}">Nom</label>
<div th:if="${#fields.hasErrors('nom')}" th:errors="*{nom}" class="text-danger">Erreur nom</div>
<input type="text" th:field="*{nom}" class="form-control" />
</div>
<div class="form-group">
<button class="btn btn-primary"><span class="glyphicon glyphicon-ok"></span>
<span th:remove="tag" th:text="#{label.add}"></span>
</button>
</div>
</form>
</th:block>
</body>
</html>