How to fill List<string> field in Thymeleaf + Spring - spring

here is my form
<!--destinationList List-->
<div class="form-group">
<div class="col-sm-3">
<label for="Destination">Destination</label>
</div>
<div class="col-sm-9">
<input type="text" class="form-control" th:field="*{destinationList[0]}" />
<input type="text" class="form-control" th:field="*{destinationList[1]}" />
<input type="text" class="form-control" th:field="*{destinationList[2]}" />
<input type="text" class="form-control" th:field="*{destinationList[3]}" />
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<button type="submit" class="btn btn-primary btn-block">Calculate</button>
</div>
</div>
</form>
And I'm going to fill following model
public class PriceSearchDTO {
private List<String> destinationList;
public List<String> getDestinationList() {
return destinationList;
}
public void setDestinationList(List<String> destinationList) {
this.destinationList = destinationList;
}
}
I can do this. But I hard coded number of input fields in the list as above in the view. I need to genarate them dynamically and make the number of element in the list is airbitary.

Try this:
<div class="col-sm-9">
<input type="text" class="form-control" th:each="destination : ${destinationList}" th:field="*{destination}" />
</div>

You are using Iteration in Thymeleaf. In fact, there is a quite complete set of objects that are considered iterable by a th:each attribute.
Like this :
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../../css/gtvg.css" th:href="#{/css/gtvg.css}" />
</head>
<body>
<h1>Product list</h1>
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="destination: ${destinations}">
<td th:text="${destination}">Onions</td>
</tr>
</table>
<p>
Return to home
</p>
</body>
</html>
And Controller in Spring framework.
#RequestMapping(value = "/")
public ModelAndView showView(ModelAndView mav) {
List<String> destinations = new ArrayList<String>();
destinations.add("elementA");
destinations.add("elementB");
destinations.add("elementC");
mav.addObject("destinations", destinations);
mav.setViewName("/viewName");
return mav;
}

Related

View and controller data exchange

I'm using form tag to get information of title and content.
But I can't get any information from form tag.
It happens to me several time. I don't know what is the problem..
this is the controller code
#Slf4j
#Controller
#RequiredArgsConstructor
public class PostController {
private final PostRepository postRepository;
private final MemberService memberService;
#GetMapping("/board/upload")
public String moveToUploadPage(Model model) {
model.addAttribute("uploadPostForm", new UploadPostForm());
return "board/uploadForm";
}
#PostMapping("/board/upload")
public String updateBoard(#ModelAttribute UploadPostForm uploadPostForm, Authentication authentication, Model model) {
log.info("uploadPostForm.title = {}", uploadPostForm.getTitle());
Member member = findMember(authentication);
Post post = new Post(member, uploadPostForm.getTitle(), uploadPostForm.getContent());
postRepository.save(post);
return "/board/board";
}
}
this is html code
<!DOCTYPE html>
<html lang="kr" xmlns:th="http://www.thymeleaf.org">
<head th:insert="~{fragment/header :: header}">
<meta charset="utf-8">
</head>
<body>
<div th:replace="~{fragment/navbar :: nav}"></div>
<!-- Form Tag -->
<form th:action th:object="${uploadPostForm}" method="post" class="container-md">
<div class="m-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" th:field="*{title}">
</div>
<div class="m-3">
<label for="content" class="form-label">Text Area</label>
<textarea class="form-control" id="content" rows="10" th:field="*{content}"></textarea>
</div>
<div class="container" style="text-align: right">
<span class="m-3" th:text="|writer: | + ${#authentication.getName()}"></span>
<button type="submit" class="btn btn-primary">upload</button>
</div>
</form>
</body>

Preserve values in modified object Thymeleaf

I'm creating an Object in GET method and initialize it with nulls. Then I set some values and send it to the POST method, but after this process all of values that I set earlier are nulls. How could I repair it? I've tried to use hidden fields, but it didn't help.
HTML code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" th:href="#{/styles.css}" />
</head>
<body>
<form method="POST" th:action="#{sendorder}" th:object="${details}">
<label for="name">Name: </label>
<input type="text" th:field="*{name}"/>
<br/>
<input type="hidden" th:field="*{order}" id="order"><label for="surname">Surname: </label>
<input type="text" th:field="*{surname}"/>
<br/><label for="room">Room: </label>
<input type="text" th:field="*{room}"/>
<br/>
<br/>
<h2>Please, choose your company:</h2>
<select th:field="*{company}">
<option th:each="i : ${companies}" th:value="${i.id}" th:text="${i.name}">
</option>
</select>
</div>
</div>
<input type="submit" value="Submit Order"/>
</form>
</body>
</html>

How to clear the error message when we click reset button in thymeleaf

I created a web page displays users information and register a new user. If we try to enter existing user it is showing the error message. when I refresh the page the error is still exist. How to clear the error message when we refresh or when we click reset button.I am using spring Boot Here is my code.
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>User Registration </title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
th:href="#{/webjars/bootstrap/3.3.6/css/bootstrap.min.css}"
rel="stylesheet" media="screen" />
</head>
<body>
<div class="container">
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-8 col-md-7 col-sm-6">
<h1>Users</h1>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>UserId</th>
<th>USerName</th>
</tr>
</thead>
<tbody border="0">
<tr th:each="user :${users>
<td th:text="${user.userId}"></td>
<td th:text="${user.userName}" > </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-7 col-sm-6">
<h1>Register USer</h1>
<form th:action="#{/registerUser}" method="post" enctype="multipart/form-data" class="form-horizontal">
<fieldset>
<div class="form-group">
<label class="col-lg-3 control-label">Enter UserId</label>
<div class="col-lg-9">
<input type="text" class="form-control" name="userId" value=""/>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">Enter UserName</label>
<div class="col-lg-9">
<input type="text" class="form-control" name="userName" value=""/>
</div>
</div>
<div class="form-group">
<div class="col-lg-12 col-lg-offset-3">
<button type="submit" class="btn btn-primary" name="button">registerUSer</button>
<button type="reset" class="btn">Reset</button>
</div>
</div>
<div th:if="*{errorMessage != null}" class="alert alert-danger" th:text="*{errorMessage}">
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
Here is my controller:
#RequestMapping(value = "/registerUser", method = RequestMethod.POST)
public String registrationForNewUSer(#RequestParam Long userId, #RequestParam String userName, Model model)
throws Exception {
//checking whether user already exist
UserModel userInfo = userInfo.findOne(userId);
if (userInfo != null) {
String str = "user already exist" + userId;
model.addAttribute("errorMessage", str);
return usersList(model);
} else {
//
return "redirect:/registerUser/" + userId + "/" + userName;
}
}
#RequestMapping(value = "/usersList", method = RequestMethod.GET)
public String usersList(Model model) {
model.addAttribute("users", userInfo.findAll());
return "usersview";
}
#RequestMapping("/registerUser/{userId}/{userName}")
public ResponseEntity<Object> authentication{
// it does authenticationprocess and add user user in database and redirect to usersview.
}

Validation and Freemarker

We have to do validation on the page, in the case of empty fields to display a message.
It's my class, the method of the controller and settings of messages.
I can't to get on the page Freemarker display error messages.
public class CreateCourseDTO {
#NotEmpty
private String name;
#NotEmpty
private String category;
#NotEmpty
private String description;
#NotEmpty
private String links;
public CreateCourseDTO() {
}
NotEmpty.createCourseDTO.name = Name is required!
NotEmpty.createCourseDTO.category = Category is required!
NotEmpty.createCourseDTO.description = Description is required!
NotEmpty.createCourseDTO.links = Links is required!
<bean class="org.springframework.context.support.ResourceBundleMessageSource"
id="messageSource">
<property name="basename" value="messages" />
</bean>
#RequestMapping(value = "/create", method = RequestMethod.POST, params = {
"name", "category", "description", "links" })
public String createCoursePost(Model model, HttpSession session,
HttpServletRequest request, #Valid CreateCourseDTO createCourseDTO,
BindingResult result) {
model.addAttribute("eMail", session.getAttribute("eMail"));
String title = request.getParameter("name");
String description = request.getParameter("description");
String links = request.getParameter("links");
String category = request.getParameter("category");
if (result.hasErrors() ) {
How do I fix freemarker page?
<#import "/spring.ftl" as spring />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" type="text/css" href="./css/style.css"/>
<style>
.error {
color: #ff0000;
}
.errorblock {
color: #000;
background-color: #ffEEEE;
border: 3px solid #ff0000;
padding: 8px;
margin: 16px;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>
Create Course
<div class="logout">
<span id="currentUserLogin">
${eMail}
</span>
<a href="logout.html">
<i class="icon-off"></i>
</a>
</div>
</h1>
</header>
<form class="form-horizontal" commandName="createCourseDTO" method=POST>
<fieldset>
<div class="control-group">
<label class="control-label">Name</label>
<div class="controls">
<input id="name" name="name" class="span5" type="text"/>
<#spring.showErrors "<br>" />
</div>
</div>
<div class="control-group">
<label class="control-label">Category</label>
<div class="controls">
<select id="category" name="category" class="span5">
<option></option>
<#list listCategories as category>
<option>${category.category}</option>
</#list>
</select>
<#spring.showErrors "<br>" />
</div>
</div>
<div class="control-group">
<label class="control-label">Description</label>
<div class="controls">
<textarea id="description" name="description" class="span5" rows="3"></textarea>
<#spring.showErrors "<br>" />
</div>
</div>
<div class="control-group">
<label class="control-label">Links</label>
<div class="controls">
<textarea id="links" name="links" class="span5" rows="3"></textarea>
<#spring.showErrors "<br>" />
</div>
</div>
<div class="form-actions">
<button id="createButton" name="createButton" class="btn btn-primary" type="submit">Create</button>
</div>
</fieldset>
</form>
<a class="btn" href="courses.html">Cancel</a>
</div>
Until that's happened.
<#import "/spring.ftl" as spring />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" type="text/css" href="./css/style.css"/>
</head>
<body>
<div class="container">
<header>
<h1>
Create Course
<div class="logout">
<span id="currentUserLogin">
${eMail}
</span>
<a href="logout.html">
<i class="icon-off"></i>
</a>
</div>
</h1>
</header>
<form action="create" method="POST"/>
<fieldset>
<#spring.bind "сreateCourseDTO" />
<#spring.showErrors '*', 'errors' />
<div class="control-group">
<label class="control-label">Name</label>
<div class="controls">
<#spring.formInput "createCourseDTO.name" ""/>
<#spring.showErrors '<br>',"error" />
</div>
</div>
<div class="control-group">
<label class="control-label">Category</label>
<div class="controls">
<#spring.formSingleSelect "createCourseDTO.category" categoryList "" />
<option></option>
<#list listCategories as category>
<option>${category.category}</option>
</#list>
</select>
<#spring.showErrors "createCourseDTO.category","error" />
</div>
</div>
<div class="control-group">
<label class="control-label">Description</label>
<div class="controls">
<#spring.formInput "createCourseDTO.description"/>
<#spring.showErrors "createCourseDTO.description","error" />
</div>
</div>
<div class="control-group">
<label class="control-label">Links</label>
<div class="controls">
<#spring.formInput "createCourseDTO.links"/>
<#spring.showErrors "createCourseDTO.links","error" />
</div>
</div>
<div class="form-actions">
<button id="createButton" name="createButton" class="btn btn-primary" type="submit">Create</button>
</div>
</fieldset>
</form>
<a class="btn" href="courses.html">Cancel</a>
</div>
</body>
</html>
Throws an error
==> assignment: status=springMacroRequestContext.getBindStatus(path) [on line 120, column 9 in spring.ftl]
in user-directive bind [on line 159, column 5 in spring.ftl]
in user-directive spring.formInput [on line 33, column 15 in pages/create.ftl]
on line <#spring.formInput "createCourseDTO.name" ""/>
and I have fixed bin
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
p:cache="true" p:prefix="/pages/" p:suffix=".ftl" p:exposeSpringMacroHelpers="true"/>

How to show a beanValidation class level constraint on a thymeleaf view?

I'm using bean validation(JSR303) to validate two fields, those fields are validate with an annotation and a validator, the annotation targeted the type that means the annotation is a class level constraint of my bean.
The question is how do I express the error on my view?
(I'm using groovy)
SignupForm :
package core.model
import core.validation.PasswordConfirmation
import core.validation.UniqueEmail
import core.validation.UniqueUsername
import groovy.util.logging.Slf4j
import org.hibernate.validator.constraints.Email
import org.hibernate.validator.constraints.NotBlank
import static core.model.AuthoritiesEnum.ROLE_USER
#Slf4j
#PasswordConfirmation
class SignupForm {
static final String NOT_BLANK_MESSAGE = "{notBlank.message}"
static final String UNIQUE_USERNAME_MESSAGE = "{uniqueUsername.message}"
static final String EMAIL_MESSAGE = "{username.message}"
static final String UNIQUE_EMAIL_MESSAGE = "{uniqueEmail.message}"
#UniqueUsername
#NotBlank(message = SignupForm.NOT_BLANK_MESSAGE)
String username
#UniqueEmail
#NotBlank(message = SignupForm.NOT_BLANK_MESSAGE)
#Email(message = SignupForm.EMAIL_MESSAGE)
String email
#NotBlank(message = SignupForm.NOT_BLANK_MESSAGE)
String password
#NotBlank(message = SignupForm.NOT_BLANK_MESSAGE)
String confirmPassword
User createAccount() {
new User(username: email, email: email, password: password, role: ROLE_USER)
}
}
PasswordConfirmation validation annotation :
package core.validation
import javax.validation.Constraint
import javax.validation.Payload
import java.lang.annotation.Retention
import java.lang.annotation.Target
import static java.lang.annotation.ElementType.TYPE
import static java.lang.annotation.RetentionPolicy.RUNTIME
#Target(TYPE)
#Retention(RUNTIME)
#Constraint(validatedBy = PasswordConfirmationValidator)
#interface PasswordConfirmation {
String message() default "{core.signup.validation.passwordConfirmation.message}"
Class<?>[] groups() default []
Class<? extends Payload>[] payload() default []
}
Password confirmation validator :
package core.validation
import core.model.SignupForm
import groovy.util.logging.Slf4j
import javax.validation.ConstraintValidator
import javax.validation.ConstraintValidatorContext
#Slf4j
class PasswordConfirmationValidator implements ConstraintValidator<PasswordConfirmation, SignupForm> {
#Override
void initialize(PasswordConfirmation constraintAnnotation) {
}
#Override
boolean isValid(SignupForm form, ConstraintValidatorContext context) {
log.info "password : ${form.password}"
log.info "confirmPassword : ${form.confirmPassword}"
println "password : ${form.password}"
println "confirmPassword : ${form.confirmPassword}"
println "defaultConstraintMessageTemplate : ${context.defaultConstraintMessageTemplate}"
form.password.equals form.confirmPassword
}
}
the thymeleaf view signup.html :
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{view.signup.title}">Inscription</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link href="../../../resources/css/bootstrap.min.css" rel="stylesheet" media="screen"
th:href="#{/resources/css/bootstrap.min.css}"/>
<link href="../../../resources/css/core.css" rel="stylesheet" media="screen" th:href="#{/resources/css/core.css}"/>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="../../../resources/js/bootstrap.min.js" th:src="#{/resources/js/bootstrap.min.js}"></script>
</head>
<body>
<div th:replace="fragments/header_signup :: header">Header</div>
<form class="form-narrow form-horizontal" method="post"
th:action="#{/signup}" th:object="${signupForm}">
<!-- /* Show general error message when form contains errors */ -->
<th:block th:if="${#fields.hasErrors('${signupForm.*}')}">
<div th:replace="fragments/alert :: alert (type='danger', message='Form contains errors. Please try again.')">
Alert
</div>
</th:block>
<fieldset>
<div class="form-group" th:classappend="${#fields.hasErrors('username')}? 'has-error'">
<label for="username" class="col-lg-2 control-label">Username</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="username" placeholder="Username" th:field="*{username}"/>
<span class="help-block" th:if="${#fields.hasErrors('username')}"
th:errors="*{username}">Incorrect username</span>
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('email')}? 'has-error'">
<label for="email" class="col-lg-2 control-label">Email</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="email" placeholder="Email address" th:field="*{email}"/>
<span class="help-block" th:if="${#fields.hasErrors('email')}"
th:errors="*{email}">Incorrect email</span>
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('password')}? 'has-error'">
<label for="password" class="col-lg-2 control-label">Password</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="password" placeholder="Password"
th:field="*{password}"/>
<span class="help-block" th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Incorrect password</span>
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('confirmPassword')}? 'has-error'">
<label for="confirmPassword" class="col-lg-2 control-label">Password confirmation</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="confirmPassword" placeholder="Password confirmation"
th:field="*{confirmPassword}"/>
<!--how to show classe level constraint validation-->
<span class="help-block" th:if="${#fields.hasErrors('confirmPassword')}" th:errors="*{confirmPassword}">Incorrect password confirmation</span>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button type="submit" class="btn btn-default" th:text="#{view.signup.label}">Sign up</button>
</div>
</div>
</fieldset>
</form>
</body>
</html>
In order to show a class level constraint message I succeeded by using the global constant, like in the updated html code as shown in the documentation
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="#{view.signup.title}">Inscription</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link href="../../../resources/css/bootstrap.min.css" rel="stylesheet" media="screen"
th:href="#{/resources/css/bootstrap.min.css}"/>
<link href="../../../resources/css/core.css" rel="stylesheet" media="screen" th:href="#{/resources/css/core.css}"/>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="../../../resources/js/bootstrap.min.js" th:src="#{/resources/js/bootstrap.min.js}"></script>
</head>
<body>
<div th:replace="fragments/header_signup :: header">Header</div>
<form class="form-narrow form-horizontal" method="post"
th:action="#{/signup}" th:object="${signupForm}">
<!-- /* Show general error message when form contains errors */ -->
<th:block th:if="${#fields.hasErrors('${signupForm.*}')}">
<div th:replace="fragments/alert :: alert (type='danger', message='Form contains errors. Please try again.')">
Alert
</div>
</th:block>
<fieldset>
<div class="form-group" th:classappend="${#fields.hasErrors('username')}? 'has-error'">
<label for="username" class="col-lg-2 control-label">Username</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="username" placeholder="Username" th:field="*{username}"/>
<span class="help-block" th:if="${#fields.hasErrors('username')}"
th:errors="*{username}">Incorrect username</span>
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('email')}? 'has-error'">
<label for="email" class="col-lg-2 control-label">Email</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="email" placeholder="Email address" th:field="*{email}"/>
<span class="help-block" th:if="${#fields.hasErrors('email')}"
th:errors="*{email}">Incorrect email</span>
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('password')}? 'has-error'">
<label for="password" class="col-lg-2 control-label">Password</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="password" placeholder="Password"
th:field="*{password}"/>
<span class="help-block" th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Incorrect password</span>
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('confirmPassword')}? 'has-error'">
<label for="confirmPassword" class="col-lg-2 control-label">Password confirmation</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="confirmPassword" placeholder="Password confirmation"
th:field="*{confirmPassword}"/>
<span class="help-block" th:if="${#fields.hasErrors('confirmPassword')}" th:errors="*{confirmPassword}">Incorrect password confirmation</span>
<span class="help-block" th:if="${#fields.hasErrors('global')}" th:errors="*{global}">Incorrect password confirmation</span>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button type="submit" class="btn btn-default" th:text="#{view.signup.label}">Sign up</button>
</div>
</div>
</fieldset>
</form>
</body>
</html>

Resources