I'm trying to create a fragment for the multiselect field in my webapp. But I'm running into various issues with regards to passing in the field that should be bound to the select element. I figure I have a syntax error, but I'm not sure what it is.
For selectedField in my addUser.jsp file, I've tried ${selectedAdminAccess}, *{selectedAdminAccess}, ${userForm.selectedAdminAccess}, etc.
And with those various combinations, in the forms.html file, I've tried ${selectField}, *{selectField}, and the preprocessor directive: ${__${selectField}__}. Have I just missed the correct combination?
The error I'm getting is "Neither BindingResult nor plain target object for bean name 'X' available as request attribute". Where X is (for the preprocessor attempt) the fieldname entered in the addUser.jsp page, or "selectField" when not using the preprocessor attempt.
Here's what I've got.
Controller
#GetMapping(value={"/adduser"}
public ModelAndView getAddUserPage(Model model, #RequstParam(required = false) Long selectedUserId) {
ModelAndView mav = new ModelAndView("/addUser");
UserForm userForm = new UserForm();
mav.addObject("userForm", userForm);
return mav;
}
model UserForm
public class UserForm {
private List<Office> adminAccess = new ArrayList<>();
private List<String> selectedAdminAccess = new ArrayList<>();
// Getter/Setter
...
}
addUser.jsp
<form method="post" action="#" data-th-action="#{/addUser}" data-th-object="${userForm}">
<!-- other interesting page stuff -->
<div data-th-replace="fragments/forms.html :: multi-select(
id='adminSelect',
selectClass='office-target',
selectField='*{selectedAdminAccess}',
optionList='*{adminAccess}',
optionListValue='id',
optionListText='name',
size=8)"></div>
</form>
forms.html
<div data-th-fragment="multi-select(id, selectClass, selectField, optionList, optionListValue, optionListText, size)" >
<select id="${id}" class="mdb-select colorful-select md-form ${selectClass}" size="${size}" multiple
searchable data-th-field="${__${selectField}__}">
<option data-th-each="option : ${adminAccess}"
data-th-value="${option.__${optionListValue}__}"
data-th-text="${option.__${optionListText}__}"></option>
</select>
</div>
The ultimate goal here is to have an expression data-th-field="*{selectedAdminAccess}" To accomplish this:
For selectField, you need to pass a string with value 'selectedAdminAccess'.
For th:field, use the preprocessing expression *{__${selectField}__}.
The HTML you provided looks like it has other problems... but I think your final HTML will look something like this:
<!-- page -->
<form method="post" action="#" data-th-action="#{/addUser}" data-th-object="${userForm}">
<!-- other interesting page stuff -->
<div data-th-replace="fragments/forms.html :: multi-select(
id='adminSelect',
selectClass='office-target',
selectField='selectedAdminAccess',
optionList=*{adminAccess},
optionListValue='id',
optionListText='name',
size=8
)" />
</form>
<!-- fragment -->
<div data-th-fragment="multi-select(id, selectClass, selectField, optionList, optionListValue, optionListText, size)" >
<select data-th-id="${id}"
data-th-class="|mdb-select colorful-select md-form ${selectClass}|"
data-th-size="${size}"
multiple
searchable
data-th-field="*{__${selectField}__}">
<option data-th-each="option : ${optionList}"
data-th-value="${option.optionListValue}"
data-th-text="${option.optionListText}" />
</select>
</div>
Using Metroids' answer above, here is the final solution that worked for me:
<!-- page -->
<form method="post" action="#" data-th-action="#{/addUser}" data-th-object="${userForm}">
<!-- other interesting page stuff -->
<div data-th-replace="fragments/forms.html :: multi-select(
id='adminSelect',
selectClass='office-target',
selectField='selectedAdminAccess',
optionList='adminAccess',
optionListValue='id',
optionListText='name',
size=8
)"></div>
</form>
<!-- fragment -->
<div data-th-fragment="multi-select(id, selectClass, selectField, optionList, optionListValue, optionListText, size)" >
<select data-th-id="${id}"
data-th-class="|mdb-select colorful-select md-form ${selectClass}|"
data-th-size="${size}"
multiple
searchable
data-th-field="*{__${selectField}__}">
<option data-th-each="option : ${__${optionList}__}"
data-th-value="${option.__${optionListValue}__}"
data-th-text="${option.__${optionListText}__}" />
</select>
</div>
The reason for the value and text to be the way they are is to allow a more dynamic object to be passed in. The text could be a name, description, or whatever. The value could be an id, or the text value, or whatever. That style I learned from https://www.northcoder.com/2019/11/thymeleaf-look-at-two-of-its-most.html and their github project linked on the page.
Crazy thing is, now that I see the answer, it was staring me in the face the whole time (it's how the value and text are being used.)
Hi Guys!
I have been implementing service in Spring Boot which
allows users to send anonymouse questionaries to server.
I have already implemented most of the backend like adding users etc. and right now I have been struggling with one action which take answers from user and sends into server (save in database).
Object containing answers (filledSurvey) is being sent as empty. In this same logic in logging users fields from form are corectly send forward.
This endpoint displays questionary:
#RequestMapping(path = {"/try", "/try/{id}"})
public String tryCompletingSurvey(Model model, #PathVariable("id") Long id) {
Connection connection = connectionService.getConnection(id);
FilledSurvey filledSurvey = connection.getSurvey().getTemplate();
for (FilledQuestion filledQuestion : filledSurvey.getFilledQuestions()) {
filledQuestion.getFilledAnswers().get(0).setCheck(true);
}
model.addAttribute("filledSurvey", filledSurvey);
return "completing/completing";
}
This is thymeleaf html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Completing survey</title>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
</head>
<body>
<center>
<form action="#" th:action="#{/user/surveys/finish}" th:object="${filledSurvey}" method="post">
<!-- <div th:each="question, questionStat : ${survey.getFilledQuestions()}" >-->
<!-- <p th:text="${question.getQuestion()}"></p>-->
<!-- <div th:each="answer, answerStat: ${question.getFilledAnswers()}" >-->
<!-- <input type="radio"-->
<!-- th:name="question+${questionStat.index}"-->
<!-- th:field="*{}"-->
<!-- th:value="${true}">-->
<!-- <label th:text="${answer.answer}">-->
<!-- </label>-->
<!-- </div>-->
<!-- </div>-->
<h2>Survey name: </h2>
<h3 th:text="${filledSurvey.getSurveyName()}"></h3>
<h2>Number of questions: </h2>
<h3 th:text="${filledSurvey.filledQuestions.size()}"></h3>
<div class="col-md-6">
<input type="submit" style="align-content: center" class="btn btn-primary" value=" Send ">
</div>
</form>
</center>
</body>
</html>
And this is endpoint which stores empty object from thymeleaf:
#RequestMapping(path = "/finish", method = RequestMethod.POST)
public String getHash(FilledSurvey filledSurvey) {
StringBuilder sb = new StringBuilder();
for (FilledQuestion question : filledSurvey.getFilledQuestions()) {
for (FilledAnswer answer : question.getFilledAnswers()) {
if (answer.isCheck()) sb.append(answer.getAnswer());
}
}
LocalDateTime date = LocalDateTime.now();
sb.append(date);
String hash = sb.toString();
hash = Base64.getEncoder().encodeToString(sb.toString().getBytes());
filledSurvey.setHash(hash);
surveyMagazinService.addSurveyToMagazin(filledSurvey);
return "completing/finish";
}
I changed code to automaticly mark answers for now.
This the picture of the object in the next endpoint:
filledSurvey object
I am aware that this is common question but i have been looking for the answer for a while now and couldn't figure it out. I have no errors in the console as well. I would appreciate any help or feedback.
If I understood correctly, I see following issue:
You are using a form to submit the survey data and use the th:object="${filledSurvey}" to bind the data. But there is actually not data send back to the controller, when the form is submitted, because there are no input fields defined that have the th:field attribute applied.
The request that will be send to the server on a submit, will contain form encoded data of all fields that you assign a th:field attribute to. The controller will map the form encoded data to a FilledSurvey object using java bean convention in the getHash method.
EDIT: can you try adding the #ModelAttribute annotation:
#RequestMapping(path = "/finish", method = RequestMethod.POST)
public String getHash(#ModelAttribute FilledSurvey filledSurvey) {
...
Try adding an input field like this inside your form:
<input type="hidden" th:field="*{surveyId}" >
This should give you at least an FilledSurvey object with the id set on your "/finish" endpoint. You can then use the id to fetch the survey like its done in the first code snippet.
The way you are using the th:field within your list of questions will not work, because spring cannot map this kind of structure. See https://spring.io/guides/gs/handling-form-submission/ to understand how form submission works with spring mvc.
I hope this helps a bit, best regards ;)
as far as I am a big fan of division frontend and backend I would like to transform my login form from Thymeleaf to REST solution. Could anyone help me with it? I got a code as following:
#Controller
public class LoginController {
#GetMapping("/login")
public String login () {
return "login";
}
}
and my form:
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<form action="" method="post">
<fieldset>
<legend>Please Login</legend>
<!-- use param.error assuming FormLoginConfigurer#failureUrl
contains the query parameter error -->
<div th:if="${param.error != null}">
Failed to login.
<div th:if="${SPRING_SECURITY_LAST_EXCEPTION != null}">
Reason: <span
th:text="${SPRING_SECURITY_LAST_EXCEPTION.message}">
</span>
</div>
</div>
<!-- the configured LogoutConfigurer#logoutSuccessUrl is /login?logout and contains the query param logout -->
<div th:if="${param.logout != null}">You have been logged out.</div>
<p>
<label for="username">Username</label> <input type="text"
id="username" name="username" />
</p>
<p>
<label for="password">Password</label> <input type="password"
id="password" name="password" />
</p>
<div>
<button type="submit" class="btn">Log in</button>
</div>
</fieldset>
My idea is to transform a controller into the REST one and than use AJAX to post the JSON with username and password. Is it a good solution? Also to which address should I send it?
Well, it's not a good approach to use thymeleaf with REST controller. Because your architecture be like, you will have one client application which calls your Server application consist of REST controller. So your design be like following
(Client Application)
[thymeleaf pages -> client controller -> client service ] => [REST controller -> Service -> repository] (Server Application)
You can refer code here if you want to go with this approach
Else you can go with a different approach in which you will have Server application with REST controller and client application with your client code with your HTML page, AJAX calls and navigation logic. This approach will be like, your client code will be in Angular/React like framework and your server-side application will have your controllers only
I have got some issue as I mentioned in the title.
This is my master page :
Admin.html
<header class="main-header">
<th:block th:include="#{admin/fragment/header}"></th:block>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<th:block th:include="#{admin/fragment/sidebar}"></th:block>
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<th:block th:include="${view}"></th:block>
</div>
<!-- /.content-wrapper -->
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>Version</b> 2.4.0
</div>
<strong>Copyright © 2014-2016 Studio.</strong> All rights
reserved.
</footer>
#{admin/fragment/header} this code will include header.html in admin/fragment directory but this file will contain dynamic code for example ,a blinking icon if there is an incoming message , the name of the logged-in user etc. , I can do this at jsp
<c:import url="/header"></c:import>
/header will call controller and then controller call will header.jsp but thymeleaf calls .html so my header will static content. I do not want to send data for header in each controller. I want to define specific controller for the header for it. How do I trigger this controller in thymeleaf ?
I want to send the url of current page to the servlet without refreshing or reloading the page. here is the code-
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Action Onclick </title>
<!-- <script>
$('#contactForm').submit(function () {
alert('sdafjb');
return false;
});
</script>-->
</head>
<body>
<form id="contactForm" >
<fieldset>
<label for="Name">Name</label>
<input id="contactName" type="text" />
</fieldset>
<fieldset>
<label for="Email">Email</label>
<input id="contactEmail" type="text" />
</fieldset>
<fieldset class="noHeight">
<textarea id="contactMessage" cols="20"></textarea>
submit
</fieldset>
</form>
<small id="messageSent">Your message has been sent.</small>
</body>
My servlet's name is scriptservlet. Please help me...
AJAX was built for communicating with a server without reloading or changing the current page in the browser. You should be able to just create an AJAX call to your server and send your server whatever data you want without affecting the current page.
Under normal circumstances, AJAX calls are restricted to "same origin" which means you can only communicate with a server on the same domain as the current web page so you would also have to make sure that you satisfy this security restriction.
Do an ajax call to the servlet and pass the Url of current page by doing (request.getRequestUri()) to the servlet.
var requestUri = '<% request.getRequestUri()%>';
var hostname = location.host;
$.ajax({
type: "POST",
url: "/scriptServlet",
data: { "host": hostname, "uri": requestUri }
}).done(function( msg ) {
alert( "Data Saved: " + msg );
});
Please note, syntax might not be correct but u have to make a ajax callt o achieve the result.
This is possible by sending back the appropriate response code (204 I guess) back to the client from the server (servlet). This way even though the request is submitted the page is not refreshed / reloaded.
You could use a hidden field in your form.
<input type="hidden" name="currentPage" value="<%=request.getRequestURL()%>">