I have a problem.
I use Spring Mongo Data, Web, mvc ecc...
I want to bind a complex object with his dependency, but my solution not work.
This is the code:
MODELS
public class Foo {
#Id
private String id;
private String nome;
private String descrizione;
private Date dataCreazione;
private Date dataUltimaModifica;
#DBRef
private Dependency dependency; //getters and setters
}
public class Dependency {
#Id
private String id;
#Indexed(unique = true)
private String nome;
private String descrizione;
private Date dataCreazione;
private Date dataUltimaModifica;
//GETTERS AND SETTERS
}
CONTROLLER
#RequestMapping(value = Constants.ADD, method = RequestMethod.GET)
public String add(Model model) {
Foo foo = new Foo();
model.addAttribute("fooForm", foo);
model.addAttribute("depList",depService.getList());
return "foo/add";
}
#RequestMapping(value = Constants.ADD, method = RequestMethod.POST)
public String addPost(#ModelAttribute("fooForm") Foo foo, BindingResult result, Model model) {
//Check if foo is not null
}
VIEW
<form:form class="form-horizontal" method="post" modelAttribute="fooForm" action="${addFoo}">
<spring:bind path="dependency">
<div class="form-group">
<label for="dependency" class="col-sm-2 control-label">Dependency</label>
<div class="col-sm-10">
<form:errors path="dependency" class="control-label" />
<form:select path="dependency" title="Dependency" >
<form:option value=""> </form:option>
<form:options items="${depList}" />
</form:select>
<p class="help-block mb-0">Select a dependency</p>
</div>
</div>
</spring:bind>
<spring:bind path="nome">
<div class="form-group">
<label for="nome" class="col-sm-2 control-label">Nome</label>
<div class="col-sm-10">
<form:errors path="nome" class="control-label" />
<form:input path="nome" type="text" class="form-control" id="nome" placeholder="Nome Foo" />
<p class="help-block mb-0">Specificare il nome of Foo</p>
</div>
</div>
</spring:bind>
<spring:bind path="descrizione">
<div class="form-group">
<label for="descrizione" class="col-sm-2 control-label">Descrizione</label>
<div class="col-sm-10">
<form:errors path="descrizione" class="control-label" />
<form:input path="descrizione" type="text" class="form-control" id="descrizione" placeholder="Descrizione" />
<p class="help-block mb-0">Inserire una breve descrizione per Foo</p>
</div>
</div>
</spring:bind>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-rounded btn-primary btn-sm">Salva</button>
</div>
</div>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
When i post the form, I get a correct value for nome and descrizione but dependency is always null... why?
Example:
//METHOD POST
public String addPost(#ModelAttribute("fooForm") Foo foo,
BindingResult result, Model model) {
foo.getNome is not null
foo.getDescrizione is not null
foo.getDependency is always null ... why?
}
Thanks in advance.
I have found the solution! It's very simple!
We need a Converter Object because the binded form return Dependency ID and not the entire Object!
Well, this is the solution:
Converter
public class DependencyConverter implements Converter {
#Autowired
private CategoriaRepository repository;
#Override
public Categoria convert(String id) {
return repository.findById(id);
}
}
Now we register the converter in our application-context.xml
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="myPackage.DependencyConverter" />
</set>
</property>
</bean>
that's all...
I hope this will be helpful in the future....
Bye Bye...
Related
I have a form for adding products written in HTML and thymeleaf.
<form th:action="#{/products/get}" th:object="${form}" method="post">
<div id="fields">
<label for="name"></label><input type="text" id="name" name="name" autofocus="autofocus" placeholder="NAME" required/><br>
<label for="label"></label><input type="text" id="label" name="label" autofocus="autofocus" placeholder="LABEL" required/><br>
Below the form, there is a button that adds two input fields to the form every time it's pressed. New input fields are the same as those two input fields above. The idea is that the user can enter data for as many products as he wants using the same form. For example, after pressing the button once the form will look like that:
<form th:action="#{/products/get}" th:object="${form}" method="post">
<div id="fields">
<label for="name"></label><input type="text" id="name" name="name" autofocus="autofocus" placeholder="NAME" required/><br>
<label for="label"></label><input type="text" id="label" name="label" autofocus="autofocus" placeholder="LABEL" required/><br>
<label for="name"></label><input type="text" id="name" name="name" autofocus="autofocus" placeholder="NAME" required/><br>
<label for="label"></label><input type="text" id="label" name="label" autofocus="autofocus" placeholder="LABEL" required/><br>
The thing is I'd like to create ArrayList of class ProductForm using the values from input fields and then pass it to my controller using #ModelAttribute.
public class ProductForm{
private String name;
private String label;
//getters and setters
}
Then created a class that wraps ProductForm into ArrayList
public class ProductFormArray {
ArrayList<ProductForm> forms;
//getters and setters
}
And a Controller
#Controller
#RequestMapping(value = "/products")
public class CreateAccountControllerTemporary {
#RequestMapping(value = "/get", method = RequestMethod.POST)
public String createAccount(#ModelAttribute(name = "form")ProductFormArray form){
//some code
}}
My problem is that I can't figure out how to add objects to form ArrayList using values from input fields? Is that even possible? How should I change my HTML file?
It is certainly possible, I explain this on pages 361 to 389 in my book Taming Thymeleaf.
You can check out the sources of the book for free at https://github.com/wimdeblauwe/taming-thymeleaf-sources/tree/main/chapter16
It is hard to summarize 30 pages into a stackoverflow answer, but briefly, check out:
CreateTeamFormData.java: This is similar to your ProductFormArray class. I do use an array instead of an ArrayList.
public class CreateTeamFormData {
#NotBlank
#Size(max = 100)
private String name;
#NotNull
private UserId coachId;
#NotNull
#Size(min = 1)
#Valid
private TeamPlayerFormData[] players;
TeamPlayerFormData.java: This is similar to your ProductForm class.
public class TeamPlayerFormData {
#NotNull
private UserId playerId;
#NotNull
private PlayerPosition position;
TeamController.java: This the controller that uses the CreateTeamFormData.
#GetMapping("/create")
#Secured("ROLE_ADMIN")
public String createTeamForm(Model model) {
model.addAttribute("team", new CreateTeamFormData());
model.addAttribute("users", userService.getAllUsersNameAndId());
model.addAttribute("positions", PlayerPosition.values()); //<.>
return "teams/edit";
}
#PostMapping("/create")
#Secured("ROLE_ADMIN")
public String doCreateTeam(#Valid #ModelAttribute("team") CreateTeamFormData formData,
BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("editMode", EditMode.CREATE);
model.addAttribute("users", userService.getAllUsersNameAndId());
model.addAttribute("positions", PlayerPosition.values());
return "teams/edit";
}
service.createTeam(formData.toParameters());
return "redirect:/teams";
}
edit.html -> This is the Thymeleaf template. Note that I am using a Thymeleaf fragment edit-teamplayer-fragment for the part of the form that repeats itself (So the name and label fields in your case)
<h3>Players</h3>
<div class="col-span-6 ml-4">
<div id="teamplayer-forms"
th:data-teamplayers-count="${team.players.length}"> <!--.-->
<th:block th:each="player, iter : ${team.players}">
<div th:replace="teams/edit-teamplayer-fragment :: teamplayer-form(index=${iter.index}, teamObjectName='team')"></div>
<!--.-->
</th:block>
</div>
<div class="mt-4">
<a href="#"
class="py-2 px-4 border border-gray-300 rounded-md text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
id="add-extra-teamplayer-form-button"
th:text="#{team.player.add.extra}"
#click="addExtraTeamPlayerForm()"
></a> <!--.-->
</div>
</div>
edit-teamplayer-fragment.html: Here is the important part where you need to keep track of the index for each fragment:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
lang="en">
<!-- tag::main[] -->
<div th:fragment="teamplayer-form"
class="col-span-6 flex items-stretch"
th:id="${'teamplayer-form-section-' + __${index}__}"
th:object="${__${teamObjectName}__}"> <!--.-->
<!-- end::main[] -->
<div class="grid grid-cols-1 row-gap-6 col-gap-4 sm:grid-cols-6">
<div class="sm:col-span-2">
<div class="mt-1 rounded-md shadow-sm">
<select class="form-select block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"
th:field="*{players[__${index}__].playerId}">
<option th:each="user : ${users}"
th:text="${user.userName.fullName}"
th:value="${user.id.asString()}">
</select>
</div>
</div>
<div class="sm:col-span-2">
<div class="mt-1 rounded-md shadow-sm">
<select class="form-select block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5"
th:field="*{players[__${index}__].position}">
<option th:each="position : ${positions}"
th:text="#{'PlayerPosition.' + ${position}}"
th:value="${position}">
</select>
</div>
</div>
<!-- tag::delete[] -->
<div class="ml-1 sm:col-span-2 flex items-center text-green-600 hover:text-green-900">
<div class="h-6 w-6">
<svg th:replace="trash"></svg>
</div>
<a href="#"
class="ml-1"
th:text="#{team.player.remove}"
x-data
th:attr="data-formindex=__${index}__"
#click="removeTeamPlayerForm($el.dataset.formindex)"> <!--.-->
</a>
</div>
<!-- end::delete[] -->
</div>
</div>
</html>
I am creating an Spring Boot App, in which the user can create licences. The user can type the name, purchaseDate, renewalDate and expirationDate.
My problem is, that when I try to save the data, it returns null for the name, the purchaseDate, renewalDate and expirationDate.
I absolutely don't know what to do anymore, please help. The "// Comment" values in the Model:Licence are those who get null when executing the post.
Model: Licence
#Entity
#Table(name = "TBL_LICENCES")
public class Licence {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private String licenceName; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String purchaseDate; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String renewalDate; // --> null after the post
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String expirationDate; // --> null after the post
public Licence() {}
public Licence(Long id, String licenceName, String purchaseDate, String renewalDate, String expirationDate) {
Id = id;
this.licenceName = licenceName;
this.purchaseDate = purchaseDate;
this.renewalDate = renewalDate;
this.expirationDate = expirationDate;
}
// Getter and Setter
}
Model: LicenceRepository
#Repository
public interface LicenceRepository extends CrudRepository<Licence, Long> { }
Service: LicenceService
#Service
public class LicenceService {
#Autowired
private LicenceRepository licenceRepository;
public List<Licence> getLicences() {
return (List<Licence>) licenceRepository.findAll();
}
public Optional<Licence> getLicenceById(Long id) {
return licenceRepository.findById(id);
}
public void addLicence(Licence licence) {
licenceRepository.save(licence);
}
public void updateLicence(Licence licence) {
licenceRepository.save(licence);
}
public void deleteLicenceById(Long id) {
licenceRepository.deleteById(id);
}
}
Controller: LicenceController
#Controller
public class LicenceController {
#Autowired
private LicenceService licenceService;
#GetMapping("/licences")
public String getLicences(Model model) {
model.addAttribute("licences", licenceService.getLicences());
return "licences";
}
#GetMapping("/onelicence")
#ResponseBody
public Optional<Licence> getLicenceByID(Long id, Model model) {
model.addAttribute("onelicence", licenceService.getLicenceById(id));
return licenceService.getLicenceById(id);
}
#RequestMapping(value="/save", method = {RequestMethod.POST, RequestMethod.PUT, RequestMethod.GET})
public String updateLicence(Licence licence) {
licenceService.updateLicence(licence);
return "redirect:/licences";
}
**// Is performed, when clicking "New Licence > Save"**
#RequestMapping(value="/addNew", method = {RequestMethod.POST, RequestMethod.PUT, RequestMethod.GET})
public String addLicence(Licence licence) {
licenceService.addLicence(licence);
return "redirect:/licences";
}
#RequestMapping(value="/delete", method = {RequestMethod.DELETE, RequestMethod.PUT, RequestMethod.GET})
public String deleteLicence(Long id) {
licenceService.deleteLicenceById(id);
return "redirect:/licences";
}
}
licences.html
<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/html"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<script type ="text/javascript" src="webjars/jquery/3.4.1/jquery.min.js"></script>
<script type ="text/javascript" src="webjars/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<link href="webjars/bootstrap/4.4.1/css/bootstrap.css" rel="stylesheet"/>
<title>Licence</title>
</head>
<body>
<div class = "container">
<h2>Licences</h2>
<table class = "table table-striped">
<thead>
<tr>
<td>ID</td>
<td>Name</td>
<td>Kaufdatum</td>
<td>Erneuerungsdatum:</td>
<td>Auslaufdatum:</td>
</tr>
</thead>
<tbody>
<tr th:each = "licence: ${licences}">
<td th:text="${licence.id}">ID</td>
<td th:text="${licence.licenceName}">Name</td>
<td th:text="${licence.purchaseDate}">Kaufdatum</td>
<td th:text="${licence.renewalDate}">Erneuerungsdatum</td>
<td th:text="${licence.expirationDate}">Auslaufdatum</td>
<td><a class="btn btn-warning">Edit</a></td>
<td><a class="btn btn-danger">Delete</a></td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addModal" data-whatever="#mdo">New Licence</button>
</div>
// Bootstrap Varying Modal Content
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<form th:action="#{/addNew}" method ="post">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">New Licence</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="name" class="col-form-label">Lizenz Name</label>
<input type="text" class="form-control" id="licenceName" name="name">
</div>
<div class="form-group">
<label for="purchase" class="col-form-label">purchaseDate</label>
<input type="date" class="form-control" id="purchaseDate" name="purchase">
</div>
<div class="form-group">
<label for="renewalAdd" class="col-form-label">renewalDate</label>
<input type="date" class="form-control" id="renewalAdd" name="renewal">
</div>
<div class="form-group">
<label for="expirationAdd" class="col-form-label">expirationDate</label>
<input type="date" class="form-control" id="expirationAdd" name="expiration">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</form>
</div>
</body>
</html>
schema.sql
DROP TABLE IF EXISTS TBL_LICENCES;
CREATE TABLE TBL_LICENCES (
id INT AUTO_INCREMENT PRIMARY KEY,
licence_name VARCHAR(250),
purchase_date VARCHAR(250),
renewal_date VARCHAR(250),
expiration_date VARCHAR(250)
);
data.sql --> This works and the data is shown.
INSERT INTO
TBL_LICENCES (licence_name, purchase_date, renewal_date, expiration_date)
VALUES
('Test1', '2020-01-31', '2020-06-31', '2020-12-31'),
('Test', '2021-01-31', '2021-06-31', '2021-12-31');
Properties: application.properties
spring.h2.console.enabled=true
spring.datasource.platform=h2
spring.datasource.url=jdbc:h2:mem:<dbLicences>
spring.jpa.hibernate.ddl-auto=update
The name attribute of the input control should match the name of the fields in the Licence class. Currently your id attribute is matching the name of the fields, but when a form is submitted it uses the name attribute to build the request parameters.
Update your HTML to match something like:
<input type="text" class="form-control" id="licenceName" name="licenceName" />
Fix the name for other fields and you will have the Licence object populated with data from the form.
Also, I see that
#DateTimeFormat(pattern = "yyyy-MM-dd")
private String licenceName;
licenceName is annotated with #DateTimeFormat. I guess this is a mistake, please correct that as well.
I'm creating a simple CRUD in Spring Boot+Thymeleaf and all other CRUD on application work well but this one. When I submit the form, the object comes with empty attributes to controller.
In the following, the code:
Artist.java
#Entity
public class Artist {
#Id
#GeneratedValue
private Long id;
#Column(nullable=false, length = 80)
private String artistName;
<getters and setters>
ArtistController.java
#Controller
public class ArtistController {
#Autowired
ArtistRepository artistRepository;
#GetMapping("/artist/add")
public String addArtist(Model model) {
model.addAttribute("artist", new Artist());
return "artist/form_artist";
}
#PostMapping("/artist/save")
public String artistSave(#ModelAttribute(name="artist") Artist artist) {
artistRepository.save(artist);
return "redirect:/artist";
}
}
And now, the form:
<form action="#" th:action="#{/artist/save}" th:object="${artist}" method="post" class="form-horizontal">
<div class="form-group">
<label for="id" >HobbyOn id:</label>
<input type="text" id="id" th:field="*{id}" readonly="readonly" class="form-control" placeholder="hobbyOn ID" />
</div>
<div class="form-group">
<label for="artistName" >Artist Name:</label>
<input type="text" th:field="*{artistName}" class="form-control" required="required" />
</div>
<div class="form-group">
<input class="btn btn-primary" type="submit" value="Submit" />
</div>
</form>
I can't see any difference from examples in internet and in my own code. Any help will be very welcomed.
I need some help. I have a trouble working on my pet project. It's simple crud app based on spring mvc. The trouble is that I can't get error messages when input validation fails in my profile.jsp. I have mostly the same code in my signup.jsp but there all works fine.
There is my controller methods:
#RequestMapping(value = "/profile", method = RequestMethod.GET)
public String getUserProfile(Model model) {
model.addAttribute("userDetailsForm", new UserDetailsFormDTO());
model.addAttribute("passwordForm" ,new PasswordFormDTO());
model.addAttribute("profile", getCurrentUser());
return "profile";
}
#RequestMapping(value = "/profile/details/change", method = RequestMethod.POST)
public String changeUserDetails(#ModelAttribute("userDetailsForm") #Valid UserDetailsFormDTO form,
BindingResult result) {
if(result.hasErrors()){
result.getAllErrors().forEach(log::debug);
return "redirect:/profile";
}
userService.changeUserDetails(getCurrentUser(), form);
return "redirect:/profile?success=details";
}
I'm using JSR 303 Validation:
package org.crud.dto;
import java.io.Serializable;
import javax.validation.constraints.Pattern;
import org.crud.validation.InputValidator;
import org.hibernate.validator.constraints.NotEmpty;
public class UserDetailsFormDTO implements Serializable{
private static final long serialVersionUID = -7603395840362468805L;
#NotEmpty(message="{error.null_form_value}")
#Pattern(regexp=InputValidator.FIRSTNAME_PATTERN, message="{error.name_invalid}")
private String firstName;
#NotEmpty(message="{error.null_form_value}")
#Pattern(regexp=InputValidator.LASTNAME_PATTERN, message="{error.name_invalid}")
private String lastName;
public UserDetailsFormDTO() {}
public UserDetailsFormDTO(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
In configuration files I have next beans declared:
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
p:validationMessageSource-ref="messageSource" />
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
Also have two .properties files with my labels and error codes on my classpath: messages_ru and messages_en.
My profile.jsp code fragment:
<div id="user-details-upd" class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Update user details</h3>
</div>
<div class="panel-body">
<div class="container-fluid">
<form name="userDetailsForm" class="form-horizontal" role="form" method="POST" action="profile/details/change">
<spring:bind path="userDetailsForm"></spring:bind>
<div class="form-group">
<div class="row">
<label for="fname-input" class="col-md-offset-2 col-md-2 control-label">First name:</label>
<div class="col-md-5">
<spring:message code="label.fname_placeholder" var="fNameHolder" />
<spring:bind path="userDetailsForm.firstName">
<input type="text" value="${profile.getFirstName()}" name="<c:out value="${status.expression}"/>" class="form-control" placeholder="${fNameHolder}" required>
<c:if test="${status.error}">
<c:forEach items="${status.errorMessages}" var="error">
<small class="text-danger"> <c:out value="${error}" />
</small>
</c:forEach>
</c:if>
</spring:bind>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<label for="lname-input" class="col-md-offset-2 col-md-2 control-label">Last name:</label>
<div class="col-md-5">
<spring:message code="label.lname_placeholder" var="lNameHolder" />
<spring:bind path="userDetailsForm.lastName">
<input type="text" value="${profile.getLastName()}" name="<c:out value="${status.expression}"/>" class="form-control" placeholder="${lNameHolder}" required>
<c:if test="${status.error}">
<c:forEach items="${status.errorMessages}" var="error">
<small class="text-danger"> <c:out value="${error}" />
</small>
</c:forEach>
</c:if>
</spring:bind>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-offset-4 col-md-5">
<button type="submit" value="Submit" class="btn btn-success">
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Update
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
And message from debug when I submit incorrect data in lastName input:
2015-08-07 18:52:29 DEBUG UserController:? - Field error in object 'userDetailsForm' on field 'lastName': rejected value [jh]; codes [Pattern.userDetailsForm.lastName,Pattern.lastName,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userDetailsForm.lastName,lastName]; arguments []; default message [lastName],[Ljavax.validation.constraints.Pattern$Flag;#657f42ee,([A-Z�-�][a-zA-Z�-�]*)([\s\'-][A-Z�-�][a-z�-�]*)*]; default message [Name must contain only characters and first letter must be in uppercase]
I can't understand, why status.errorMessages is empty when BindingResult has got errors?
Any help or suggestions are appreciated.
Thanks in advance
You are using redirects within the request mapping block of your controller. The redirect is a header sent to the browser. The browser initiates the redirect, consequently you get a totally new request from the browser and because http is stateless, you lose anything stored in that previous request/response, such as the BindingResult.
remove the redirects and use a string to forward to the jsp page. You can use the internalviewresolver for this
I am trying to add validation to my Spring MVC Application. Previously to attempting to set up validation I have been using ModelAndView to serve up the jsp pages but the error messages do not display.
Model
#Entity
#Table(name = "employee")
public class Employee {
#Id
#NotEmpty(message = "Please enter your email addresss.")
#Email
private String email;
#NotEmpty
#Pattern(regexp="[a-zA-Z0-9]")
private String password;
#NotEmpty
private String firstName;
#NotEmpty
private String lastName;
#NotEmpty
private String phoneNum;
private boolean assigned;
public Employee() {
}
// getters and setters
}
Controller
#RestController
#RequestMapping("/employee")
public class EmployeeController {
private static final Logger LOGGER = LogManager
.getLogger(EmployeeController.class.getName());
#Autowired
private EmployeeServiceImpl employeeService;
#RequestMapping(value = "/new", method = RequestMethod.GET)
public ModelAndView getRegisterView(Model m) {
return new ModelAndView("addEmployee", "employeeForm", new Employee());
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ModelAndView postUser(#Valid Employee employee, BindingResult result) {
ModelAndView model;
if (result.hasErrors()) {
model = new ModelAndView("addEmployee", "employeeForm", employee);
} else {
employeeService.addEmployee(employee);
model = new ModelAndView();
model.setViewName("displayEmployee");
model.addObject("employees", employeeService.getEmployees());
}
return model;
}
}
Form
<form:form class="form-horizontal" role="form" method="POST"
action="register" commandName="employeeForm" modelattribute="employee">
<div class="form-group">
<form:label path="firstName"
class="control-label col-lg-2 col-lg-offset-2 col-md-2 col-md-offset-2 col-sm-12"
for="fname">First Name:</form:label>
<div class="col-lg-6 col-md-6 col-sm-12">
<form:input path="firstName" class="form-control" id="fname"
placeholder="First Name" />
<form:errors path="firstName" />
</div>
</div>
<div class="form-group">
<form:label path="lastName"
class="control-label col-lg-2 col-lg-offset-2 col-md-2 col-md-offset-2 col-sm-12"
for="lname">Last Name:</form:label>
<div class="col-lg-6 col-md-6 col-sm-12">
<form:input path="lastName" class="form-control" id="lname"
placeholder="Last Name" />
<form:errors path="lastName" />
</div>
</div>
<div class="form-group">
<form:label path="email"
class="control-label col-lg-2 col-lg-offset-2 col-md-2 col-md-offset-2 col-sm-12"
for="email">Email:</form:label>
<div class="col-lg-6 col-md-6 col-sm-12">
<form:input path="email" type="email" class="form-control"
id="email" placeholder="Email" />
<form:errors path="email" cssClass="error" />
</div>
</div>
<div class="form-group">
<form:label path="phoneNum"
class="control-label col-lg-2 col-lg-offset-2 col-md-2 col-md-offset-2 col-sm-12"
for="phoneNum">Phone Number:</form:label>
<div class="col-lg-6 col-md-6 col-sm-12">
<form:input path="phoneNum" class="form-control" id="phoneNum"
placeholder="Phone Number" />
<form:errors path="phoneNum" />
</div>
</div>
<div class="form-group">
<form:label path="password"
class="control-label col-lg-2 col-lg-offset-2 col-md-2 col-md-offset-2 col-sm-12"
for="pwd">Password:</form:label>
<div class="col-lg-6 col-md-6 col-sm-12 controls">
<form:input path="password" type="password" class="form-control"
id="pwd" placeholder="Password" />
<form:errors path="password" />
</div>
</div>
<div class="form-group form-actions">
<div
class="col-lg-offset-4 col-lg-1 col-md-offset-4 col-md-1 col-sm-1">
<button type="submit" class="btn btn-primary">Register</button>
</div>
<div class="col-lg-1 col-md-1 col-sm-1">
<button type="reset" class="btn btn-primary">Clear</button>
</div>
</div>
<form:hidden path="assigned" value="false" />
</form:form>
model = new ModelAndView("addEmployee", "employeeForm", employee);
You are destroying the model yourself in your code, so when returning to the page there is basically nothing left. When returning a ModelAndView Spring MVC assumes that you have prepared and added everything needed for rendering the view yourself.
Instead add #ModelAttribute("employeeForm") to your method argument next to the #Valid annotation and use the already existing model from the BindingResult to construct a ModelAndView.
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ModelAndView postUser(#Valid #ModelAttribute("employeeForm" Employee employee, BindingResult result) {
ModelAndView model;
if (result.hasErrors()) {
model = new ModelAndView("addEmployee", result.getModel());
Although this will work why not simply return a String as the name of the view and do some Model preparation when needed.
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String postUser(#Valid #ModelAttribute("employeeForm") Employee employee, BindingResult result, Model model) {
if (result.hasErrors()) {
return "addEmployee";
} else {
employeeService.addEmployee(employee);
model.addObject("employees", employeeService.getEmployees());
return "displayEmployee";
}
}
I would even argue that filling the Model for the displayEmployee page doesn't belong here but in a separate method preparing the model for that page.