Java spring boot - retrieve dynamic inputs - java

I'm trying to build a small QCM Application:
You have an quiz that have multiple questions and therefore multiple potential answer. The user can select only one answer between all (using radio buttons).
The user submit the form, but now i'm trying to retrieve the answers that the user selected on the server but I have not much clue on what to do because those fields are dynamic.
<form method="post" th:action="#{/score}" class="qcm-questions">
<input type="hidden" name="id_quiz" th:value="${id_quiz}" />
<input type="hidden" name="pseudo" th:value="${pseudo}" />
<fieldset class="qcm-questions-item" th:each="question: ${questions}">
<h2 th:text="${question.getLabel()}"></h2>
<small th:text="'Question ' + ${questionStat.index}"></small>
<div th:each="answer: ${question.getAnswers()}">
<label th:text="${answer.getLabel()}" th:for="${answer.getId_answer()}"></label>
<input type="radio" th:id="${answer.getId_answer()}" th:name="${question.getId_question()}" th:value="${answer.getId_answer()}" />
</div>
</fieldset>
<button>Soumettre QCM</button>
</form>
The only method I found is this :
#PostMapping
public String registerScore(#RequestParam("id_quiz") final long id_quiz, #RequestParam("pseudo") final String pseudo, ServletRequest request) {
Map<String, String[]> answers = request.getParameterMap();
answers.remove("id_quiz");
answers.remove("pseudo");
return "page";
}
Maybe you have a better idea than this ?

You can retrieve inputs from a form through parameters or modelattributes.
I found personnally the model attribute solution easier and below is a code to do it like so.
I have created 2 modelattributes that I pass to the controller.
The first one will contain all the quiz details and the second one will be the answers collected in you form.
Modelattributes are objects and of course you can adapt the entities.
#Controller
public class FormQuizController {
#GetMapping ("/quiz")
public String main(Model model) {
Question quest1 = new Question();
quest1.setId(1);
quest1.setLabel("What is the answer?");
Answer answer1 = new Answer (1, "answer 1");
Answer answer2 = new Answer (2, "answer 2");
Answer answer3 = new Answer (3, "answer 3");
Answer[] answers = {answer1, answer2, answer3};
quest1.setAnswers(answers);
Question quest2 = new Question();
quest2.setId(2);
quest2.setLabel("What is the answer again?");
quest2.setAnswers(answers);
Question[] questions = {quest1, quest2};
Quiz quiz = new Quiz();
quiz.setId_quiz(1);
quiz.setPseudo("Quiz 1");
quiz.setQuestions(questions);
ResultQuiz resultQuiz = new ResultQuiz(0,"init", new int[questions.length],new int[questions.length]);
model.addAttribute("quiz", quiz);
model.addAttribute("resultQuiz", resultQuiz);
return "quiz";
}
#PostMapping ("/score")
public String score(#ModelAttribute ResultQuiz resultQuiz) {
System.out.println(resultQuiz);
return "score";
}
}
The method main is creating a quiz with 2 questions and 3 answers for each questions. It is also creating a resultQuiz initialized with fake data.
the method score is retrieving the result of the quiz through the object ResultQuiz.
Below the classes for domain (getters/setters/constructors omitted for brevity):
public class Quiz {
private int id_quiz;
private String pseudo;
private Question[] questions;
}
public class Question {
private int id;
private String label;
private Answer[] answers;
}
public class Answer {
private int id;
private String text;
}
public class ResultQuiz {
private int id_quiz;
private String pseudo;
private int[] id_question = {0,0};
private int[] id_answer = {0,0};
}
Then the code for the html pages using thymeleaf.
th:object indicates the object linked to the form. Then you can bind data directly.
To access values of an attribute with thymeleaf you just need to indicate the name of it.
iter variable gives you the index of each iteration.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>test form</title>
</head>
<body>
<form method="post" th:action="#{/score}" th:object="${resultQuiz}" class="qcm-questions">
<input type="hidden" th:name="'id_quiz'" th:value="${quiz.id_quiz}" >
<input type="hidden" th:name="'pseudo'" th:value="${quiz.pseudo}" >
<p th:text="'Valeur de resultQuiz:'+${resultQuiz}"></p>
<fieldset class="qcm-questions-item" th:each="question, iter: ${quiz.questions}">
<h2 th:text="${question.label}"></h2>
<small th:text="'Question ' + ${iter.index}"></small>
<input type="hidden" th:name="'id_question['+${iter.index}+']'" th:value="${question.id}" >
<div th:each="answer: ${question.answers}">
<label>
<span th:text="${answer.text}" th:for="${answer.text}"></span>
<input type="radio" th:name="'id_answer['+${iter.index}+']'" th:value="${answer.id}" />
</label>
</div>
</fieldset>
<button type="submit">Soumettre QCM</button>
</form>
</body>
</html>
And finally the code for the score html page:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>test form</title>
</head>
<body>
<span th:text="'For Quiz '+${resultQuiz.id_quiz}+' with name '+${resultQuiz.pseudo}"></span>
<h2>Results are:</h2>
<p th:each="question, iter: ${resultQuiz.id_question}">
<span th:text="'For question '+${question} +' answer is '+${resultQuiz.id_answer[iter.index]}"></span>
</p>
</body>
</html>

Related

How do i pass two th:object in a single form

I'm new to thymeleaf and couldn't found a way to pass two th:objects in a single HTML form. how can I pass two different entity objects.in this case, my entities are Patient and Doctor. My form is given below.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<title>LOG IN PAGE</title>
</head>
<body>
<div class="container">
<form th:action="#{/loggedProfile}" th:object="${patient,doctor}"
method="get">
<div class="form-group">
<label for="patientEmail">Email address</label> <input type="email"
class="form-control" placeholder="Enter Your email"
th:field="*{eMail}"> <small id="emailHelp"
class="form-text text-muted">We'll never share your email
with anyone else.</small>
</div>
<div class="form-group">
<label for="patientPassword">Password</label> <input type="password"
class="form-control" placeholder="Enter Your Password"
th:field="*{password}">
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
</div>
</body>
<div th:insert="Header-Footer/common_header_footer"></div>
</html>
Here is my controller, its raw.
#GetMapping("/logIn")
public String logIn(Model model) {
Patient patient = new Patient();
model.addAttribute("patient", patient);
Doctor doctor = new Doctor();
model.addAttribute("doctor", doctor);
return "UI-Pages/LogIn_Page";
}
#GetMapping("/loggedProfile")
public String loggedProfile(#ModelAttribute Doctor doctor,#ModelAttribute Patient patient,Model model) {
doctor = docRep.findByeMailAndPassword(doctor.geteMail(), doctor.getPassword());
patient = patRep.findByeMailAndPassword(patient.geteMail(), patient.getPassword());
model.addAttribute("doctor", doctor);
model.addAttribute("patient", patient);
return "Profile-Pages/Patient_Profile";
}
Thanks.
You create a new object that contains a Patient and a Doctor.
public class LoginForm {
private Patient patient;
private Doctor doctor;
// getters and setters
}
Then access the fields like this:
<form th:action="#{/loggedProfile}" th:object="${form}"
th:field="*{patient.eMail}"
....
th:field="*{doctor.password}"
</form>
Etc...
It is probably better to use a dedicated Data Transfer Object that maps to the fields in the form data:
public class LoginFormData {
private String email;
private String password;
}
Then transform from the Patient or Doctor entity into LoginFormData and back in your controller.
Side note:
Use th:method="post" in the form with a #PostMapping in the controller if it is the intention to use the form to change data
Having a method findByeMailAndPassword is not something that you should have. Passwords should be in the database in plain text, and it would be strange to try to find a certain user with its plain text password.

Display data on HTML using Thymeleaf

I would like to display the data I get from a search, personalized for my taste. At this moment, It is just plain text.
For example, I am searching for "Titanic", and I get the name, a few links, and some information from IMDB.
I have the following code:
search.html
<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Form</h1>
<form action="#" th:action="#{/search}" th:object="${search}" method="post">
<p>Message: <input type="text" th:field="*{content}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</body>
</html>
result.html
<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Result</h1>
<p th:text="'content: ' + ${main.content}"></p>
Submit another message
</body>
</html>
SearchController.java
#Controller
public class SearchController {
#GetMapping("/search")
public String greetingForm(Model model) {
model.addAttribute("search", new Main());
model.addAttribute("main", new Main().getContent());
return "search";
}
#PostMapping("/search")
public String greetingSubmit(#ModelAttribute Main main) {
return "result";
}
}
and Main.java
private String content;
private List<Result> finalList;
private List<Result> resultList;
public void setContent(String content) throws IOException {
//code to compute finalList
}
public List<Result> getContent() {
return this.finalList;
}
The main problem is that I have no ideea where to being with. finalList is a list of objects of type "Result", which have fields such as
private List<String> link = new ArrayList<>();
private String name;
private TitleProp titleProp;
and TitleProp has
private String trailer;
private String rating;
private String description;
private String genre;
I would like to manipulate each field to show it on a different way, such as a table with more rows, etc.
Any link or sample of code would help me a lot to understand Thymeleaf and Spring Boot more.
I am coming with an answer to my question. I managed to get the result I wanted using Ajax, as SnakeDoc suggested. It was a long road, mostly because even if I had a working code, I spent a few hours searching for the Forbidden 403 error on ajax post request.
So, for the js part:
function ajaxPost() {
// Here we prepare data for the JSON
var formData = {
moviename: $("#moviename").val()
}
$.ajax({
type: "POST",
contentType: "application/json",
url: "MYURL",
data: JSON.stringify(formData),
dataType: 'json',
success: function (result) {
{
$.each(result,
function (i, title) {
// do whatever you want with what you got from the server
});
console.log("Success: ", result);
}
console.log(result);
},
error: function (e) {
console.log("ERROR: ", e);
}
});
}
If this seems confusing, I access the fields you can see in my question by
title.name, title.link, title.titleProp.description, etc, inside function (i, title)'s body.
For the HTML,
<label for="moviename" style="margin-right:5px">Title:</label>
<input type="text" class="form-control" id="moviename" placeholder="Enter a title"/>
Where moviename is the variable name you get from the input.
Now, on the backend, we have to configure our path
#PostMapping("/MYURL")
public ResponseEntity<Object> addSearch(#RequestBody SearchCriteria searchCriteria)
throws IOException {
// do whatever you want to get a result. I used a custom class "SearchCriteria"
// which has a getter and a setter for the field
// **private String moviename;**
return ResponseEntity.ok(THIS GETS SENT TO AJAX);
}
The main problem was that I have web.security, and you have two choices. First one, you disabling csrf. You have to add this line to your security config.
http.csrf().disable();
in protected void configure(HttpSecurity http) method.
Or, you add csrf to the ajax request. More info on this topic was discussed here
With thymeleaf, you can display a list in html like so:
<tr th:each="student: ${students}">
<td th:text="${student.id}" />
<td th:text="${student.name}" />
</tr>
More info: https://www.baeldung.com/thymeleaf-iteration

Using ftl display list from POST method

Sorry for my english!
I am new to Spring and FTL.
I want to display firstName and lastName using <#list template, but I could not recognize any sequences in my POST method, could some one please explain to me. Again I am a newbie and please don't judge me if I don't understand what I should. I am using CUBA STUDIO 6.8 and IDEA. Also I'm working on this task in portal module
This is how I add firstName and lastName to database using my ftl form and Portal Controller:
#GetMapping("/add")
public String add(Model model){
PersonPojo personPojo = new PersonPojo();
model.addAttribute("personPojo", personPojo);
return "add";
}
#PostMapping("/add")
public String save(Model model, #ModelAttribute("personPojo") PersonPojo personPojo){
String firstName = personPojo.getFirstName();
String lastName = personPojo.getLastName();
PersonPojo newPerson = new PersonPojo(firstName, lastName);
Person standardEntity = metadata.create(Person.class);
standardEntity.setFirtName(newPerson.getFirstName());
standardEntity.setLastName(newPerson.getLastName());
dataManager.commit(standardEntity);
return "redirect:/allPersons";
}
My ftl form:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" name="person">
First Name: <input type="text" name="firstName"> <br>
Last Name: <input type="text" name="lastName"> <br>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
<input type="submit" value="Create">
</form></body>
</html>
Thank you!
So, If someone is interested i will post my solution here:
#RequestMapping(value = "/allPersons", method = RequestMethod.GET)
public String getPersons(Model model) {
LoadContext loadJohn = new LoadContext(John.class);
loadJohn.setQueryString("select u from test6$John u");
model.addAttribute("users", dataService.loadList(loadJohn));
return "list";
}
And the ftl should look like this:
The problem i faced next was I did not know I have to check list for null. !"" does that
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>Person List</h3>
Add Person
<br><br>
<div>
<table border="1">
<tr>
<th>First Name</th>
<th>Last Name</th>
</tr>
<#list users as show>
<tr>
<td>${show.firstName!""}</td>
<td>${show.lastName!""}</td>
</tr>
</#list>
</table>
</div>
</body>
</html>
I hope this will help to people like me.
Also, If someone knows how to delete and update data please share.
Thanks!

Why am I getting null object after submitting form in Spring?

I am new to Spring and yesterday I created a simple app. I type book's title, author and genre and it saves it to List<Book>. Book.java contains private fields (title, author, genre).
So creating books and saving them to list works fine. Also I can view all books I have added. So it works fine. But now I want to create and delete them. So I have BookService.java that can add and delete books.
BookService.java
private List<Book> allBooks = new ArrayList<>();
public List<Book> getAllBooks() {
return allBooks;
}
public void addBook(String title, String author, String genre) {
allBooks.add(new Book(title, author, genre));
}
public void deleteBook(Book book) {
allBooks.remove(book);
}
This is stuff in my controller to delete books
#GetMapping("/books/delete")
public String deleteBook(Model model) {
model.addAttribute("BookList", bookService.getAllBooks()); // Works fine
return "delete";
}
#PostMapping("/books/delete")
public String deletedBook(#ModelAttribute Book book, Model model) {
System.out.println(book.getTitle()); // null
bookService.deleteBook(book); // can't delete null so nothing happens to the list
model.addAttribute("deletedBook", book);
return "deletedBookResult";
}
delete.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2>Delete page</h2>
<div th:each="bookObj : ${BookList}"> <!-- BookList - all books I add using submit form -->
<form action="#" th:action="#{/books/delete}" th:object="${bookObj}" method="post"> <!-- I SEND bookObj -->
<input type="submit" th:value="${bookObj.title}"/> <!-- WORKS. I GET BOOK'S NAME ON THIS BUTTON-->
</form>
</div>
</body>
</html>
I use th:each="bookObj : ${BookList}". So bookObj is every book I add. That's why I use th:object=${bookObj}. There is a form for each book I added later. And it display's it's title on the button. But when I press it, I get null to IDEA's console and on webpage. Why?
Thank you in advance.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2 th:text=" 'You have just deleted' + ${deletedBook.title}"></h2>
<!-- You have just deleted null -->
</body>
</html>
You are not sending anything in your form. Add some hidden fields:
<div th:each="bookObj : ${BookList}">
<form action="#" th:action="#{/books/delete}" method="post">
<input type="hidden" th:name="genre" th:value="${bookObj.genre}"/>
<input type="hidden" th:name="author" th:value="${bookObj.author}"/>
<input type="hidden" th:name="title" th:value="${bookObj.title}"/>
<input type="submit" th:value="${bookObj.title}"/>
</form>
</div>
Or if you don't want to expose your data in html, you can try to use some sort of session objects instead (Probably an overkill, but sometimes can be useful):
#GetMapping("/books/delete")
public String deleteBook(Model model, HttpSession session) {
session.setAttribute("BookList", new Book[]{
new Book("Title", "Tom","genre"),
new Book("Title 2", "Jerry","genre2")}
);
return "delete";
}
#PostMapping("/books/delete")
public String deletedBook(HttpSession session, Integer id, Model model) {
Book[] books = (Book[]) session.getAttribute("BookList");
Book book = books[id];
System.out.println(book);
model.addAttribute("deletedBook", book);
return "deletedBookResult";
}
And use it like:
<div th:each="bookObj, iter : ${session.BookList}">
<form action="#" th:action="#{/books/delete}" method="post">
<input type="hidden" th:name="id" th:value="${iter.index}"/>
<input type="submit" th:value="${bookObj.title}"/>
</form>
</div>

JSP Display User Details not working at all

<!--
To change this template, choose Tools | Templates
and open the template in the editor.
-->
<!DOCTYPE html>
<html>
<head>
<title>Mail Registration</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="newcss.css">
<link rel="text/javascript" href="validateForm.js">
</head>
<body>
<div id="container">
<div id="header">
<h1>Online Book Store Mailing Registration</h1>
</div>
<div id="content">
<div id ="leftSide">
<p>Welcome to the Online Book Store Registration.
In order to join our mailing list you must complete the form. Then press the Submit button.</p>
</div>
<div id="rightSide">
<h2>Thanks for joining our email list</h2>
<h3>Here is the information that you entered:</h3>
<%# page import="user.User" %>
<% User user = (User) request.getAttribute("User");%>
<table cellspacing="5" cellpadding="5" border="1">
<tr>
<th align="right">First Name:</th>
<th>${user.getFirstName}</th>
</tr>
<tr>
<th align="right">Last Name:</th>
<th>${user.getLastName}</th>
</tr>
<tr>
<th align="right">Town:</th>
<th>${user.getTown}</th>
</tr>
<tr>
<th align="right">Country:</th>
<th>${user.getCountry}</th>
</tr>
<tr>
<th align="right">Email Address:</th>
<th>${user.getEmailAddress}</th>
</tr>
</table>
</form>
<br />
</div>
</div>
<div id="footer">
<h2>xxx</h2>
</div>
</div>
</body>
</html>
This is my first time working with JSP, I have to display user details that have been added to database. I have been looking for some time now at other questions asked here about displaying details and I have not found an answer yet.
I have java class called User.java in user Package.
If anyone could point me where I went wrong I would be thankful.
I have this in my servlet
String firstName = request.getParameter("firstName");
String lastName = request.getParameter("lastName");
String town = request.getParameter("town");
String country = request.getParameter("country");
String emailAddress = request.getParameter("emailAddress");
// create the User object
User User = new User();
User.setFirstName(firstName);
User.setLastName(lastName);
User.setTown(town);
User.setCountry(country);
User.setEmailAddress(emailAddress);
MailDB.insert(User);
request.setAttribute("User", User);
String url = "/return_user_details.jsp";
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(url);
dispatcher.forward(request, response);
I suppose that your User class is like this :
Class User {
private String firstName;
private String lastName;
private String country;
.
.
.
/*generating getters & setters*/
public String getFirstName(){
return firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
}
So the problem you're having is here ${user.getFirstName} this will never work unless your attribute is named getFirstName which I don't think you did so to solve this issue you simply have to :
replace
${user.getFirstName} with ${user.firstName} , generally use the attribute name and not the getters and setters methods name.

Categories

Resources