How to validate a form with thymeleaf and spring? - spring

I simply want to validate a form data and display an error message. Validating email format, password size and password matching. What would be the best way to do that?
Here's what I've tried
My controller
#PostMapping("/{customerId}/createUser")
public String signIn(#PathVariable(value = "customerId", required = false) Long customerId,
#ModelAttribute(name = "user") #Valid Users user, RedirectAttributes redirectAttributes,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "customerNewUser";
}
// Encrypt password
user.setPassword(encoder.encode(user.getPassword()));
user.setCustomerId(customerId);
user.setEventDescription("User Admin creation");
try {
Users returnedUser = userService.save(user);
List<Authorities> authorities = new ArrayList<Authorities>();
Authorities auth = new Authorities(new AuthoritiesPK(returnedUser.getId(), "ROLE_CLI_ADM"), returnedUser,
"ROLE_CLI_ADM");
authorities.add(auth);
returnedUser.setAuthorities(authorities);
returnedUser.setEventDescription("Add Admin role");
for (int i = 0; i < returnedUser.getAuthorities().size(); i++) {
authorityService.save(returnedUser.getAuthorities().get(i));
}
userService.save(returnedUser);
} catch (WebExchangeBindException webe) {
StringBuilder errorBuilder = new StringBuilder();
for (ObjectError error : webe.getAllErrors()) {
errorBuilder.append(messageSource.getMessage(error.getCode(), null, Locale.getDefault())).append("\n");
}
redirectAttributes.addFlashAttribute("signInError", errorBuilder.toString());
}
return "redirect:/customers/?id=" + customerId;
}
My form in my customerNewUser html file
<form
th:action="#{${user?.id} ? '/customers/' + ${customer.id} + '/createUser/' + ${user.id} : '/customers/' + ${customer.id} + '/createUser/'}"
th:object="${user}" action="#" method="post">
<div class="alert alert-danger" th:if="${#fields.hasAnyErrors()}">
<div th:each="detailedError : ${#fields.detailedErrors()}">
<span th:text="${detailedError.message}"></span>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="col-md-5">
<label class="text-right label-control"
th:text="#{firstname} + ':'">Firstname:</label>
</div>
<div class="col-md-4">
<input type="text" id="firstname" name="firstname"
th:value="${user?.firstname} ? ${user?.firstname} : ''"
class="form-control" th:placeholder="#{firstname}"
required="required" autofocus="autofocus" th:field="*{firstname}"/>
<label th:style="'color: red;'" class="text-right label-control" th:if="${#fields.hasErrors('firstname')}" th:errors="*{firstname}" th:text="#{passwordError}">Password Error</label>
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="col-md-5">
<label class="text-right label-control"
th:text="#{surname} + ':'">Surname:</label>
</div>
<div class="col-md-4">
<input type="text" id="surname" name="surname"
th:value="${user?.surname} ? ${user?.surname} : ''"
class="form-control" th:placeholder="#{surname}"
autofocus="autofocus" />
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="col-md-5">
<label class="text-right label-control"
th:text="#{email} + ':'">Email:</label>
</div>
<div class="col-md-4">
<input th:placeholder="#{email}" required="required"
autofocus="autofocus" id="email" class="form-control"
type="text" th:value="${user?.email} ? ${user?.email} : ''"
name="email" th:field="*{email}"/>
<label th:style="'color: red;'" class="text-right label-control" th:if="${#fields.hasErrors('email')}" th:errors="*{email}" th:text="#{emailError}">Email Error</label>
</div>
</div>
</div>
<div class="form-group" th:if="!${user?.id}">
<div class="form-row">
<div class="col-md-5">
<label class="text-right label-control"
th:text="#{password} + ':'">Password:</label>
</div>
<div class="col-md-4">
<input type="password" id="password" name="password"
class="form-control" th:placeholder="#{password}"
required="required" autofocus="autofocus" th:field="*{password}"/>
<label th:style="'color: red;'" class="text-right label-control" th:if="${#fields.hasErrors('password')}" th:errors="*{password}" th:text="#{passwordError}">Password Error</label>
</div>
</div>
</div>
<div class="form-group" th:if="!${user?.id}">
<div class="form-row">
<div class="col-md-5">
<label class="text-right label-control"
th:text="#{passwordConfirmation} + ':'">Password confirmation:</label>
</div>
<div class="col-md-4">
<input type="password" id="matchPassword" name="matchPassword"
class="form-control" th:placeholder="#{password}"
required="required" autofocus="autofocus" th:field="*{matchPassword}"/>
<label th:style="'color: red;'" class="text-right label-control" th:if="${#fields.hasErrors('matchPassword')}" th:errors="*{matchPassword}" th:text="#{matchPassword}">Match Password Error</label>
</div>
</div>
</div>
<div class="form-group" th:if="${user?.id}">
<div class="form-row">
<div class="col-md-5">
<label class="text-right label-control"
th:text="#{userStatus} + ':'">Status:</label>
</div>
<div class="col-md-3">
<select class="form-control form-control" id="userStatus"
name="userStatus">
<option
th:each="userStatus : ${T(br.com.macrosul.stetho.entity.UserStatus).values()}"
th:text="${userStatus}" th:value="${userStatus}"
th:selected="${user.userStatus} eq ${userStatus}"></option>
</select>
</div>
</div>
</div>
<div class="form-group">
<p class="error-control" th:text="${signInError}"></p>
</div>
<input type="submit" class="btn btn-md btn-block"
value="Sign in"
th:value="${user?.id} ? #{updateUser} : #{signIn}" />
</form>
And here is the stacktrace when I try to force a password size error ->
#Size(min=6)
#Column(nullable = false)
private String password;
Field error in object 'user' on field 'password': rejected value [12345]; codes [Size.user.password,Size.password,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.password,password]; arguments []; default message [password],2147483647,6]; default message [tamanho deve estar entre 6 e 2147483647]
I want a simple validation. If there's a simpler way to indicate the user the data are invalid I would appreciate for any help or advice!

It is a little bit late, but you need to put your BindingResult next to your #valid attribute so instead of:
#ModelAttribute(name = "user") #Valid Users user, RedirectAttributes redirectAttributes, BindingResult bindingResult
you should write:
#ModelAttribute(name = "user") #Valid Users user, BindingResult bindingResult, RedirectAttributes redirectAttributes

You need a validator like below. Its not the exact code what you need but sufficient enough and pretty close to what you need to get you going.
#Override
public void validate(Object o, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "useremail", "NotEmpty");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userfirstname", "NotEmpty");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userlastname", "NotEmpty");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "useraddress", "NotEmpty");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty");
if (user.getPassword().length() < 8 || user.getPassword().length() > 32) {
errors.rejectValue("password", "Size.userForm.password");
}
if (!user.getPasswordConfirm().equals(user.getPassword())) {
errors.rejectValue("passwordConfirm", "Diff.userForm.passwordConfirm");
}
}
For more details you can take a look at here, here and here.

You can put a div in the html that only exists if there are errors on the validation. Then you show each error in a loop:
<div th:if="${#fields.hasErrors('*')}">
<p th:each="err : ${#fields.errors('*')}">
<span th:text="${err}"></span>
</p>
</div>
This way, when you return to this view there will be errors on your fields so this code will be shown.

Related

How can I do calculation based on other fields in thymeleaf?

I am working with Spring boot, Spring-data, ThymeLeaf. I have some fields. "Passenger Name", "Age", "Source", "Destination", "No of tickets", "Ticket Price", "Discount".
The following html code:
<div class="form-group has-error has-feedback" data-z="1b5278b0" id="passengerName1" data-th-classappend="${#fields.hasErrors('passengerName')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="passengerName" class="col-md-3 control-label" data-th-text="#{label_ticketbooking_passengerName}">passengerName</label>
<div class="col-md-3">
<input id="passengerName" name="passengerName" data-th-value="*{{passengerName}}" type="text" class="form-control inputmask" placeholder="passengerName" data-th-placeholder="#{label_ticketbooking_passengerName}" data-toggle="tooltip" />
</div>
</div>
<div class="form-group has-error has-feedback" data-z="1b5278b0" id="age-field" data-th-classappend="${#fields.hasErrors('age')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="age" class="col-md-3 control-label" data-th-text="#{label_ticketbooking_age}">age</label>
<div class="col-md-3">
<input id="age" name="age" data-th-value="*{{age}}" type="text" class="form-control inputmask" placeholder="age" data-th-placeholder="#{label_ticketbooking_age}" data-toggle="tooltip" data-inputmask-alias="numeric" data-inputmask-digits="0" min="1" />
</div>
</div>
<div class="form-group has-error has-feedback" data-z="1b5278b0" id="source1" data-th-classappend="${#fields.hasErrors('source')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="source" class="col-md-3 control-label" data-th-text="#{label_ticketbooking_source}">source</label>
<div class="col-md-3">
<input id="source" name="source" data-th-value="*{{source}}" type="text" class="form-control inputmask" placeholder="source" data-th-placeholder="#{label_ticketbooking_source}" data-toggle="tooltip" />
</div>
</div>
<div class="form-group has-error has-feedback" data-z="1b5278b0" id="destination1" data-th-classappend="${#fields.hasErrors('destination')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="destination" class="col-md-3 control-label" data-th-text="#{label_ticketbooking_destination}">destination</label>
<div class="col-md-3">
<input id="destination" name="destination" data-th-value="*{{destination}}" type="text" class="form-control inputmask" placeholder="destination" data-th-placeholder="#{label_ticketbooking_destination}" />
</div>
</div>
<div class="form-group has-error has-feedback" data-z="1b5278b0" id="noOfTickets-field" data-th-classappend="${#fields.hasErrors('noOfTickets')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="noOfTickets" class="col-md-3 control-label" data-th-text="#{label_ticketbooking_noOfTickets}">noOfTickets</label>
<div class="col-md-3">
<input id="noOfTickets" name="noOfTickets" data-th-value="*{{noOfTickets}}" type="text" class="form-control inputmask" placeholder="noOfTickets" data-th-placeholder="#{label_ticketbooking_noOfTickets}" data-toggle="tooltip" aria-describedby="noOfTicketsStatus" data-inputmask-alias="numeric" data-inputmask-digits="0" min="1" />
</div>
</div>
<div class="form-group has-error has-feedback" data-z="ed99c550" id="ticketPrice-field" data-th-classappend="${#fields.hasErrors('ticketPrice')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="ticketPrice" class="col-md-3 control-label" data-th-text="#{label_ticketbooking_ticketPrice}">ticketPrice</label>
<div class="col-md-3">
<input id="ticketPrice" name="ticketPrice" data-th-value="*{{ticketPrice}}" type="text" class="form-control inputmask" placeholder="ticketPrice" data-th-placeholder="#{label_ticketbooking_ticketPrice}" data-toggle="tooltip" />
</div>
</div>
<div class="form-group has-error has-feedback" data-z="d1a1d590" id="ticketdiscount-field" data-th-classappend="${#fields.hasErrors('ticketDiscount')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="ticketDiscount" class="col-md-3 control-label" data-th-text="#{label_ticketbooking_ticketdiscount}">ticketDiscount</label>
<div class="col-md-3">
<input id="ticketDiscount" name="ticketDiscount" data-th-value="*{{ticketDiscount}}" type="text" class="form-control inputmask" placeholder="ticketDiscount" data-th-placeholder="#{label_ticketbooking_ticketdiscount}" data-toggle="tooltip" data-inputmask-alias="numeric" data-inputmask-digits="0" />
</div>
</div>
<div class="form-group has-error has-feedback" data-z="ed99c550" id="totalPrice-field" data-th-classappend="${#fields.hasErrors('totalPrice')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="totalPrice" class="col-md-3 control-label" data-th-text="#{label_ticketbooking_totalPrice}">totalPrice</label>
<div class="col-md-3">
<input id="totalPrice" name="totalPrice" data-th-value="*{{totalPrice}}" type="text" class="form-control inputmask" placeholder="totalPrice" data-th-placeholder="#{label_ticketbooking_totalPrice}" data-toggle="tooltip" />
</div>
</div>
Here totalPrice should be calculated based on totalPrice = (noOfTickets * ticketPrice ) - ticketDiscount
Note: ticketDiscount may applicable or not. If applicable need to minus else no need to subtract it.
How can I achieve this?
There are several things you should take into account and several approaches you can use. Lets simplify things a little bit and suppose you have
Form DTO
#Data
public class TestDto {
private int ticketPrice;
private int noOfTickets;
private int ticketDiscount;
}
and Controller
#Controller
public class TestController {
#RequestMapping(name = "/", method = RequestMethod.GET)
public ModelAndView get() {
TestDto dto = new TestDto();
dto.setNoOfTickets(10);
dto.setTicketPrice(12);
return new ModelAndView("main", "dto", dto);
}
#RequestMapping(name = "/", method = RequestMethod.POST)
public String post(#ModelAttribute("dto") TestDto dto) {
System.out.println(dto);// can process input values
return "main";
}
}
Important. I assume you haveth:object="${dto}" in you form. If you don't, then just use dto.fieldName instead of fieldName like dto.ticketPrice instead of ticketPrice and $ instead of *
Option 1. Use Thymeleaf syntax. totalPrice will change after each form submit (POST request)
<form action="/" th:object="${dto}" method="post">
<input type="number" th:id="ticketPrice" th:field="*{ticketPrice}"/>
<input type="number" th:id="noOfTickets" th:field="*{noOfTickets}"/>
<input type="number" th:id="ticketDiscount" th:field="*{ticketDiscount}"/>
<span th:text="*{noOfTickets * ticketPrice - (ticketDiscount != 0 ? ticketDiscount: 0)}"
th:id="totalPrice"/>
<input type="submit" value="Subscribe!"/>
</form>
Option 2. Calculate value in java code POST changes to the server is also required to update the total value. Simple case can just use method with all the calculations in your DTO. This option works only if you have all the info for your calculations in this DTO
public class TestDto {
// ... same code as before
public int getTotalPrice() {
return noOfTickets * ticketPrice - (ticketDiscount != 0 ? ticketDiscount: 0);
}
}
This is easy to use just like any other field in your dto
<span th:text="*{totalPrice}"></span>
<span th:text="${dto.totalPrice}"></span>
<span th:text="*{getTotalPrice()}"></span>
If you need some extra info for your calculations, you probably can use service as suggested by #mrtasln. And for our simple case it can look like:
#Service("myService")
public class MyServices {
// Option 1
public int calculateTotal(MyDto dto){
return dto.getNoOfTickets() * dto.getTicketPrice() - (dto.getTicketDiscount() != 0 ? dto.getTicketDiscount(): 0);
}
// Option 2
public int calculateTotal2(int noOfTickets, int ticketPrice, int ticketDiscount){
return noOfTickets * ticketPrice - (ticketDiscount != 0 ? ticketDiscount: 0);
}
}
And xml part can be something like one of:
<span th:id="totalPriceFromService"
th:text="${#myService.calculateTotal(dto)}"></span>
<span th:id="totalPriceFromService2"
th:text="*{#myService.calculateTotal2(ticketPrice, noOfTickets, ticketDiscount)}"></span>
<span th:id="totalPriceFromService2"
th:with="tp=*{ticketPrice},nt=*{noOfTickets},td=*{ticketDiscount}"
th:text="${#myService.calculateTotal2(tp, nt, td)}"></span>
Option 3. The Javascript way is the only dynamic option to calculate changes. No need to perform POST to update the total value.
You can use some library to help you there, but simple case should
Define some JavaScript function like calculateTotal()
Put oninput="calculateTotal()" attribute on each input field you want to listen
Something like this:
<form action="/" th:object="${dto}" method="post">
<input type="number" th:id="ticketPrice" th:field="*{ticketPrice}" oninput="calculateTotal()"/>
<input type="number" th:id="noOfTickets" th:field="*{noOfTickets}" oninput="calculateTotal()"/>
<input type="number" th:id="ticketDiscount" th:field="*{ticketDiscount}" disabled="disabled"/>
<span th:id="totalPriceJS"></span>
<input type="submit" value="Subscribe!"/>
</form>
<script type="text/javascript">
function calculateTotal() {
var price = document.getElementById("ticketPrice").value;
var quantity = document.getElementById("noOfTickets").value;
var discount = document.getElementById("ticketDiscount").value;
var totalInput = document.getElementById("totalPriceJS");
//do all the calculations here
var total = price * quantity
if (discount) total -= discount;
totalInput.innerHTML = total
}
calculateTotal(); // don't forget to call this function on the first run
</script>
Try this calculation:
<div th: "${ticketDiscount !=null} ? result=${noOfTickets * totalPrice - ticketDiscount } : result=${noOfTickets * totalPrice}">
<span th:text="${result}"></span>
</div>
Here I am checking the value of ticketdiscount is null or not.
In Spring , you can do it with using service.Example code below :
Service class :
#Component("calculateService")
public class CalculateService {
public Integer calculateTotalPrice(Integer noOfTickets ,Integer ticketPrice, Integer ticketDiscount ){
return (noOfTickets * ticketPrice ) - ticketDiscount;
}
}
Html File :
<span th:text="${#calculateService.calculateTotalPrice(noOfTickets ,ticketPrice ,ticketDiscount)}"></span>

How to do server side validation in spring mvc using validator class

Hi am new to spring mvc i have to do sever side validation using separate validator class. i have two model with one to many relation i have to register them before register them i am new server side validation in spring mvc to use spring form and all
<form id="studentEnrollmentForm" method="post" class="form-horizontal" action="saveStudentByAdmin">
<div class="form-group">
<label class="col-xs-2 control-label">Student Full Name</label>
<div class="group">
<div class="col-xs-3">
<input type="text" class="form-control" name="studentFirstName" id="fn" placeholder="First name" />
</div>
</div>
<div class="col-xs-3">
<input type="text" class="form-control" name="studentMiddleName" placeholder="Middle name" />
</div>
<div class="col-xs-3">
<input type="text" class="form-control" name="studentLastName" placeholder="Last name" />
</div>
</div>
<div class="form-group">
<label class="col-xs-2 control-label">Parents Full Name</label>
<div class="col-xs-3">
<input type="text" class="form-control" name="parentFirstName" placeholder="First name" />
</div>
<div class="col-xs-3">
<input type="text" class="form-control" name="parentMiddleName" placeholder="Middle name" />
</div>
<div class="col-xs-3">
<input type="text" class="form-control" name="parentLastName" placeholder="Last name" />
</div>
</div>
<div class="form-group">
<label class="col-xs-2 control-label">Date-of-birth</label>
<div class="col-xs-3 ">
<div class="input-group input-append date" id="studentDOB">
<input type="Text" class="form-control" name="studentDOB" /> <span
class="input-group-addon add-on"><span
class="glyphicon glyphicon-calendar"></span></span>
</div>
</div>
<label class="col-xs-3 control-label">Gender</label>
<div class="col-xs-3">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-default">
<input type="radio" name="studentGender" value="male" />Male</label>
<label class="btn btn-default">
<input type="radio" name="studentGender" value="female" />Female</label>
</div>
</div>
</div>
<div class="form-group">
<label class="col-xs-2 control-label">Phone</label>
<div class="col-xs-3">
<input type="text" class="form-control" name="parentPhoneNumber" placeholder="Phone number" />
</div>
<label class="col-xs-3 control-label">Email</label>
<div class="col-xs-3">
<input type="text" class="form-control" name="parentEmail" placeholder="Email" />
</div>
</div>
<div class="form-group">
<label class="col-xs-2 control-label">Permanent Address</label>
<div class="col-xs-3">
<textarea class="form-control" rows="3" name="studentPermanentAddress" /></textarea>
</div>
<label class="col-xs-3 control-label">Present Address</label>
<div class="col-xs-3">
<textarea class="form-control" rows="3" name="studentPresentAddress" /></textarea>
</div>
</div>
<div class="form-group">
<label class="col-xs-2 control-label">Class to join</label>
<div class="col-xs-3">
<!-- <input type="text" class="form-control" name="className" placeholder="Enter Class" /> -->
<select name="className" class="form-control">
<option value="">Select class </option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-xs-1 ">
<button type="submit" class="btn btn-primary ">Submit</button>
</div>
<div class="col-xs-1 ">
<button type="reset" class="btn btn-default" id="rstbutton">Refresh</button>
</div>
</div>
</form>
Controller
#Controller
#RequestMapping("/")
public class AdminRegistrationController {
#Autowired
private IAdminRegistrationService adminRegistrationService;
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String viewRegistrationPage(Model model) {
StudentDTO studentDTO = new StudentDTO();
ParentDTO pdto=new ParentDTO();
model.addAttribute("teacherDTO", studentDTO);
model.addAttribute("teacherDTO", pdto);
return "StudentEnrollmentByAdmin";
}
#RequestMapping(value = "/saveStudentByAdmin", method = RequestMethod.POST)
public String saveTeacherByAdmin(#ModelAttribute StudentDTO sdto,#ModelAttribute ParentDTO pdto) {
System.out.println(sdto.getStudentFirstName());
System.out.println(pdto.getParentFirstName());
return "redirect:/register"; // this line redirecting to above method to avoid same data insertion again when i press f5
//return "TeacherEnrollmentByAdmin"; To know duplication insertion comment above line and above method current this line
//when you get response page(after insertion of data) press f5 and see in data base
}
}
My Example validator class
#Component
public class StudentRegistrationFromAdminValidator implements Validator{
public boolean supports(Class<?> clazz) {
return StudentDTO.class.isAssignableFrom(clazz);
//return ParentDTO.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
StudentDTO student = (StudentDTO)target;
ParentDTO parent = (ParentDTO)target;
String studentFirstName = student.getStudentFirstName();
String parentFirstName=parent.getParentFirstName();
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "studentFirstName", "student.studentFirstName.empty");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "parentFirstName", "parent.parentFirstName.empty");
if(studentFirstName.equals("pradee")){
errors.rejectValue("studentFirstName", "student.studentFirstName.invalid");
}
if(parentFirstName.equals("pradee")){
errors.rejectValue("parentFirstName", "parent.parentFirstName.invalid");
}
}
}
In StudentDTO and ParentDTO classes, you can give various validator annotations from javax.validation.constraints package and org.hibernate.validator.constraints package(if using hibernate).
public class StudentDTO {
#Size(min=6, max=12)
private String firstName;
#Size(min=6, max=12)
private String lastName;
//getter setter
}
Then add javax.validation.Valid annotation and
org.springframework.validation.BindingResult interface in controller method as follows.
#RequestMapping(value = "/saveStudentByAdmin", method = RequestMethod.POST)
public String saveTeacherByAdmin(#Valid #ModelAttribute StudentDTO sdto,#ModelAttribute ParentDTO pdto,
BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
System.out.println(bindingResult.getAllErrors().get(0).getDefaultMessage());
}
}

How tell the view an error has occured from controller? (Spring MVC)

How can I say trigger error/validation messages in the view from the controller in a better way? Currently, I do this by sending boolean attributes. For example, in creating a product, I have two possible errors. Invalid format of UPC of a product, or duplicate upc. I also have a validati
#RequestMapping("/createProduct")
public String createProduct(Model model, #RequestParam(value = "name") String name,
#RequestParam(value = "upc") String upc, #RequestParam(value = "category") String categoryName,
#RequestParam(value = "description") String description, #RequestParam(value = "price") BigDecimal price,
#RequestParam(value = "stock") int stock){
model.addAttribute("activeTab", 3);
if(Validator.invalidUpcFormat(upc)){
model.addAttribute("invalidFormat", true); //trigger for invalid format
return "management";
}
Category category = productService.getCategory(categoryName);
Product product = new Product(upc, category, name, description, price);
InventoryProduct inventoryProduct = new InventoryProduct(product, stock);
try {
managerService.add(inventoryProduct);
model.addAttribute("productCreated", true);
} catch (DuplicateProductException e) {
model.addAttribute("upc", upc);
model.addAttribute("duplicateProduct", true); // trigger for duplicate product
}
return "management";
}
And here is my view:
<div id="menu3"
class="tab-pane fade <c:if test="${activeTab == 3}">in active</c:if>">
<div class="container-fluid" style="padding: 2%;">
<div class="row">
<div class="col-md-12"
style="padding-left: 15%; padding-right: 15%;">
<c:if test="${productCreated}">
<div class="alert alert-success fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Success!</strong>
Product has been created!
</div>
</c:if>
<c:if test="${duplicateProduct}">
<div class="alert alert-warning fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Oh no!</strong>
Product with the UPC ${upc} already exists!
</div>
</c:if>
<c:if test="${invalidFormat}">
<div class="alert alert-warning fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Oops!</strong>
Invalid UPC format!
</div>
</c:if>
<form
action="${pageContext.request.contextPath}/manager/createProduct"
method="post">
<div class="form-group">
<label for="Name">Name </label> <input type="text" name="name"
class="form-control" required />
</div>
<div class="form-group">
<label for="UPC">UPC </label> <input type="number" name="upc"
class="form-control" required />
</div>
<div class="form-group">
<div class="form-group">
<label for="category">Category</label> <select
class="form-control" name="category" required>
<option selected disabled value="">SELECT CATEGORY</option>
<c:forEach items="${categories}" var="item">
<option>${item.getName()}</option>
</c:forEach>
</select>
</div>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" rows="5" name="description"></textarea>
</div>
<div class="form-group">
<label for="price">Price </label> <input type="number"
name="price" class="form-control" required />
</div>
<div class="form-group">
<label for="stock">Stock </label> <input type="number"
name="stock" class="form-control" required />
</div>
<button type="submit" class="btn btn-primary">Add
product</button>
</form>
</div>
</div>
</div>
</div>
Is there a better of doing this other than sending boolean triggers?
You could use Spring BindingResult. This is typical filled with the result of Binding and Validation results. But you can also add errors by hand.
But first you need to refactor your code, so that you use an single command/form-backing object instead of all the #Param values
public class CreateProductCommand {
private String name;
private String upc;
private String categoryName;
.... //other fields
public CreateProductCommand (){} //parameter less conturctor
Getter+Setter
}
Controller
#RequestMapping("/createProduct")
public ModelAndView createProduct(CreateProductCommand createProductCommand, BindingResult bindingResult) //Binding result must be the parameter direct next to the object that should been validated!!!
{
if (someustomValidationForUcpFail()) {
bindingResult.rejectValue("upc", //the field name of the invalid field
"error.Message.Key",
"Default Error Message");
}
if (bindingResult.hasErrors()) {
ModelMap model = new ModelMap();
model.add("createProductCommand", createProductCommand);
return new ModelAndView("createForm", model)
} else {
Product product = ...
return new ModelAndView("showProduct", "product", product)
}
}
jsp:
You need to use springs form and input tag:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:springForm="http://www.springframework.org/tags/form"
version="2.0">
....
<springForm:form action="<c:url value="/manager/createProduct">" method="POST" modelAttribute="createProductCommand">
<springForm:input path="name"/> <form:errors path="name" />
<springForm:input path="ucp"/> <form:errors path="ucp" />
....
</springForm:form>
....

Cannot submit a form on SpringMVC

I am fairly new to SpringMVC and have a form that can not submit to the back-end. I created the form as following and when I submit it error 404 will be returned. I changed action to /MyProject/contact but did not work.
<form class="form-horizontal" role="form" method="post"
action="/contact">
<div class="form-group">
<div class="col-md-12">
<label class="sr-only" for="exampleInputName2">Name
</label> <input type="text" class="form-control" id="name"
name="name" placeholder="Your name" value="">
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<label class="sr-only" for="exampleInputName2">Email
Address</label> <input type="email" class="form-control" id="email"
name="email" placeholder="Your email" value="">
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<label class="sr-only" for="exampleInputName2">Phone
Number</label> <input type="number" class="form-control" id="phone"
name="phone" placeholder="Phone number" value="">
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<label class="sr-only" for="exampleInputName2">Enquiry</label>
<textarea class="form-control" rows="4" name="message"
placeholder="Please enter your enquiry"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-md-2 " style="float: right;">
<input id="submit" name="submit" type="submit" value="Send"
class="btn btn-primary">
</div>
</div>
<div class="form-group">
<div class="col-sm-10 col-sm-offset-2">
<! Will be used to display an alert to the user>
</div>
</div>
</form>
Controller
#Controller
public class ContactController {
#RequestMapping(value="/contact", method=RequestMethod.POST)
public String processForm(Contact contact, Model model){
System.err.println("Contact Name is:" + contact.getName());
return null;
}
}
Error
HTTP Status 404 - /contact
type Status report
message /contact
description The requested resource is not available.
Its beacuse spring does not know how to pass the param Contact contact to your controller method. You need to do couple of things to make it work. Change your form to like below.
<form class="form-horizontal" role="form" method="post" modelAttribute="contact" action="/contact">
Your controller to take contact as model attribute.
#Controller
public class ContactController {
#RequestMapping(value="/contact", method=RequestMethod.POST)
public String processForm(#ModelAttribute Contact contact, Model model){
System.err.println("Contact Name is:" + contact.getName());
return null;
}
}
For a better understanding of what a model attribute does, there are plenty of samples and explanation online. Hope this helps.
I could solve the problem by help of minion's answer, following this tutorial and adding following link
#RequestMapping(value = "/contact", method = RequestMethod.GET)
public ModelAndView contactForm() {
System.err.println("here in GET");
return new ModelAndView("contact", "command", new Contact());
}

How to use jsp tag for select?

I have a view model and one of the fields has to be mapped to a dropdown. I manage to populate the dropdown but i don't know how to specify the selected value.
edituser.jsp
<%# include file="/WEB-INF/template/taglibs.jsp"%>
<div class="container">
<%# include file="menu.jsp"%>
<form:form commandName="editForm" method="post" class="form-horizontal form-width">
<fieldset>
<legend>Edit user</legend>
<div class="form-group">
<input class="hidden" type="text" value="${id}" hidden="true" name="id"/>
<label for="firstName" class="col-lg-2 control-label">First
name</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="firstName"
placeholder="First name" name="firstName" value="${fname}">
</div>
</div>
<div class="form-group">
<label for="lastName" class="col-lg-2 control-label">Last
name</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="lastName"
placeholder="Last name" name="lastName" value="${lname}">
</div>
</div>
<div class="form-group">
<label for="select" class="col-lg-2 control-label">Role</label>
<div class="col-lg-10">
<select name="role" class="form-control">
<option value="NONE">Select role</option>
<c:forEach items="${roles}" var="role">
<option value="${role}">${role}</option>
</c:forEach>
</select>
</div>
</div>
<div class="form-group">
<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" name="username" value="${username}">
</div>
</div>
<div class="form-group">
<label for="inputPassword" class="col-lg-2 control-label">Password</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="inputPassword"
placeholder="Password" name="password" value="${guid}">
</div>
</div>
<div class="form-group">
<label for="inputPassword" class="col-lg-2 control-label">Confirm password</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="inputPassword"
placeholder="Password" name="confirmedpassword" value="${guid}">
</div>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<a class="btn btn-warning" href="http://localhost:8080/Catering/index/users">Cancel</a>
<button type="submit" class="btn btn-warning">Submit</button>
</div>
</div>
</fieldset>
</form:form>
</div>
UsersController
#Controller
public class UsersController {
#RequestMapping(value = "/editUser", method = RequestMethod.GET)
public String getEditUserPage(#RequestParam int id, Model model, Authentication authentication) {
logger.debug("Received request to show edit user page(GET)");
model.addAttribute("username", "You are logged in as " + authentication.getPrincipal());
UserModel user = UserDataAccess.getUserById(id);
model.addAttribute("id", user.getId());
model.addAttribute("fname", user.getFirstName());
model.addAttribute("lname", user.getLastName());
model.addAttribute("role", user.getRole());
model.addAttribute("username", user.getUsername());
model.addAttribute("guid", guid);
model.addAttribute("editForm", new UserViewModel());
return "edituser";
}
}
UserViewModel
public class UserViewModel {
private int id;
private String firstName;
private String lastName;
private String role;
private String username;
private String password;
private String confirmedpassword;
//getters and setters
}
You can do something like (not tested)
<select name="role" class="form-control">
<option value="NONE">Select role</option>
<c:forEach items="${roles}" var="curRole">
<c:choose>
<c:when test="${curRole eq role}">
<option selected="selected">${role}</option>
</c:when>
<c:otherwise>
<option>${role}</option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
You also must add a Collection in your controller, representing the list of all roles. I cannot see it from your code.
I suggest you to take a look at spring-form.tld facilities, in your particular case: select tags: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-form.tld.html#spring-form.tld.select
Here you can find a simple example on how using spring:form tag: http://www.mkyong.com/spring-mvc/spring-mvc-dropdown-box-example/

Resources