Spring MVC Validation w/ ModelAndView - java

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.

Related

Spring Boot and Thymeleaf Error: Could not parse Exception

I am creating a TO-DO list using Spring Boot, Spring Data, Thymeleaf. Can anyone tell me if my syntax is correct. This is my first application using spring boot. I am getting this error org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "" (index)
This is the index.html
<div th:if test="${mode=='MODE_HOME'}">
<div class="container" id ="homeDiv">
<div class="jumbotron text-center">
<h1>Welcome to Tasks Manager</h1>
</div>
</div>
</div>
<div th:if test="${mode=='MODE_TASKS'}">
<div class="container text-center" id ="tasksDiv">
<h3>My Tasks</h3>
<hr/>
<div class="table-responsive">
<table class="table table-striped table-bordered text-left">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th>Date</th>
<th>Finished</th>
</tr>
</thead>
<tbody>
<tr th:each="task: ${tasks}">
<td th:text="${task.id}"></td>
<td th:text="${task.name}"></td>
<td th:text="${task.description}"></td>
<td th:text="${task.dateCreated}"></td>
<td th:text="${task.finished}"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div th:if test="${mode=='MODE_NEW' || mode=='MODE_UPDATE'}">
<div class="container text-center">
<h3>Manage Task</h3>
<hr/>
<form class="form-horizontal" method="POST" action="save-task">
<input type="hidden" name="id" value="${task.id}"/>
<div class="form-group">
<label class="control-label col-md-3">Name</label>
<div class="col-md-7">
<input type="text" class="form-control" name="name" placeholder="name" value="${task.name}"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">Description</label>
<div class="col-md-7">
<input type="text" class="form-control" name="description" placeholder="name" value="${task.description}"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">Finished</label>
<div class="col-md-7">
<input type="radio" class="col-sm-1" name="finished" placeholder="finished" value="true"/>
<div class="col-sm">Yes</div>
<input type="radio" class="col-sm-1" name="finished" placeholder="finished" value="false"/>
<div class="col-sm">No</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Save"/>
</div>
</div>
</form>
</div>
</div>
This is the MainController
#Controller
#RequestMapping("/")
public class MainController {
#Autowired
private TaskService taskService;
#RequestMapping(method = RequestMethod.GET)
public String home(Model model){
model.addAttribute("mode", "MODE_HOME");
return "index";
}
#RequestMapping(method = RequestMethod.GET, value = "/all-tasks")
public String allTasks(Model model){
model.addAttribute("tasks", taskService.findAll());
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
#RequestMapping(method = RequestMethod.GET, value = "/new-task")
public String newTask(Model model){
//List<Task> taskList = taskService.findAll();
model.addAttribute("mode", "MODE_NEW");
return "index";
}
#RequestMapping(method = RequestMethod.GET, value = "/save-task")
#PostMapping("/save-task")
public String saveTask(#ModelAttribute Task task, Model model) {
taskService.save(task);
model.addAttribute("tasks", taskService.findAll());
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
#RequestMapping(method = RequestMethod.GET, value = "/update-task")
public String updateTask(#RequestParam int id, Model model){
model.addAttribute("tasks", taskService.findTask(id));
model.addAttribute("mode", "MODE_UPDATE");
return "index";
}
#RequestMapping(method = RequestMethod.GET, value = "/delete-task")
public String deleteTask(#RequestParam (required = false) int id, Model model){
taskService.delete(id);
model.addAttribute("tasks", taskService.findAll());
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
}

Thymeleaf+Spring Boot: form emptying object attributes

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.

Spring Form binding object one to many

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...

How to bind an input value in a list to a bean attribute with Spring

I need to bind an object with a list of objects as an attribute. It is a static list and it is created and partially filled in the Controller. It is displayed correctly in the view, however the value in the input field is not set when the form is sent to the controller. I did some research and found several similar questions, however none of the proposed solutions worked for me.
edit.jsp
<c:forEach items="${priceConfigurationForm.priceList}" var="price" varStatus="priceStatus">
<tr>
<td>
<spring:bind path="priceConfigurationForm.priceList[${priceStatus.index}].country">
${price.country}
<input type="hidden" name="<c:out value="${status.expression}"/>"
id="<c:out value="${status.expression}"/>"
value="<c:out value="${status.value}"/>"/>
</spring:bind>
</td>
<td>
<spring:bind path="priceConfigurationForm.priceList[${priceStatus.index}].amount">
<input type="text" name="price" value="${price.amount}"/>
<input type="hidden"
name="<c:out value="${status.expression}"/>"
id="<c:out value="${status.expression}"/>"
value="<c:out value="${status.value}"/>"/>
</spring:bind>
</td>
<td>
<spring:bind path="priceConfigurationForm.priceList[${priceStatus.index}].currency">
${price.currency}
<input type="hidden" name="<c:out value="${status.expression}"/>"
id="<c:out value="${status.expression}"/>"
value="<c:out value="${status.value}"/>"/>
</spring:bind>
</td>
</tr>
</c:forEach>
Abstract from Controller populating the list:
#RequestMapping(value = "/price/create", method = RequestMethod.GET)
public String toCreatePriceConfiguratioView(Model model) {
log.info("::createPriceConfiguration: {}");
final PriceConfigurationForm priceConfigurationForm = new PriceConfigurationForm();
final List<PriceConfigurationForm.Price> prices = new ArrayList<>();
for (Country country : Country.values()) {
PriceConfigurationForm.Price price = new PriceConfigurationForm.Price();
price.setAmount(100);
price.setCountry(country.getCountryCode());
price.setCurrency(country.getCurrency());
prices.add(price);
}
priceConfigurationForm.setPriceList(prices);
model.addAttribute("priceConfigurationForm", priceConfigurationForm);
return "/secured/sources/onboarding/price/edit";
}
Abstract from Controller for storing the list:
#RequestMapping(value = "/price/create", method = RequestMethod.POST)
public String createPriceConfiguration(Model model, #ModelAttribute("priceConfigurationForm") #Valid PriceConfigurationForm priceConfigurationForm, BindingResult bindingResult, RedirectAttributes attributes) {
log.info("::createPriceConfiguration {}", priceConfigurationForm);
// TODO: Validate, before create
/* transform form to domain object */
PriceConfiguration configuration = PriceConfigurationForm.toPriceConfiguration(priceConfigurationForm);
onboardingApi.createPriceConfiguration(configuration);
attributes.addFlashAttribute("message", success("price configuration saved", priceConfigurationForm.getName()));
return "/secured/sources/onboarding/price/index";
}
Object:
#Data
public class PriceConfigurationForm {
private String name;
private String description;
private List<Price> priceList;
private Map<String, Long> countryToPriceMap;
public static PriceConfiguration toPriceConfiguration(PriceConfigurationForm form) {
final PriceConfiguration pc = new PriceConfiguration();
pc.setName(form.getName());
pc.setDescription(form.getDescription());
final Map<String, Long> prices = form.getPriceList().stream().collect(toMap(Price::getCountry, Price::getAmount));
pc.setCountryToPriceMap(prices);
return pc;
}
#Data
public static class Price {
private String country;
private long amount;
private String currency;
}
}
This is example of how I sucessfully parsed nested objects in one project :
<c:forEach var="block" items="${newplaceform.blocks}" varStatus="counter">
<fieldset data-block-order="${counter.index}" data-block-index="${counter.index}">
<c:if test='${block.type=="quizSingleAnswer"}'>
<legend><s:message code='blocks.type.quizSingleAnswer'/> <a class="glyphicon glyphicon-move move-block pull-right" href="#"></a><a data-remove="block" class="glyphicon glyphicon-remove pull-right" href="#"></a><a data-reordering="up" class="glyphicon glyphicon-chevron-up pull-right" href="#"></a><a data-reordering="down" class="glyphicon glyphicon-chevron-down pull-right" href="#"></a></legend>
<div class="form-group production-hide">
<label class="col-sm-3 control-label">id:</label>
<div class="col-sm-9"><input type="text" name="blocks[${counter.index}].id" value="${block.id}" data-row="blockId" data-disabled class="form-control"/></div>
</div>
<div class="form-group production-hide">
<label class="col-sm-3 control-label">type:</label>
<div class="col-sm-9"><input type="text" name="blocks[${counter.index}].type" value="${block.type}" data-disabled class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label"><s:message code='blocks.description'/>:</label>
<div class="col-sm-9"><textarea class="form-control" name="blocks[${counter.index}].description" data-description rows="3">${block.description}</textarea></div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="col-sm-6 control-label">
<label class="inner-header"><s:message code='blocks.answers'/>:</label>
</div>
<div class="col-sm-6">
<div class="btn-group m-t-9">
<a class="btn btn-primary btn-xs" href="#" data-add-answer data-add-answer-block-index="${counter.index}"><i class="glyphicon glyphicon-plus"></i> <s:message code="blocks.add-answer" /></a>
</div>
</div>
</div>
</div>
<div class="row quiz-answers" data-count-answer="${block.answers.size()}">
<c:forEach var="answer" items="${block.answers}" varStatus="counterInner">
<div class="col-sm-6">
<fieldset>
<legend>
<div class="bootstrap-center">
<span><s:message code="blocks.answerNo"/> ${counterInner.index+1}</span>
<a data-remove="answer" class="glyphicon glyphicon-remove pull-right" href="#"></a>
</div>
</legend>
<div class="form-group production-hide">
<label class="col-sm-6 control-label">id:</label>
<div class="col-sm-6"><input type="text" name="blocks[${counter.index}].answers[${counterInner.index}].id" value="${answer.id}" data-row="answerId" data-disabled class="form-control"/></div>
</div>
<div class="form-group">
<label class="col-sm-6 control-label"><s:message code="blocks.answer"/>:</label>
<div class="col-sm-6"><input type="text" name="blocks[${counter.index}].answers[${counterInner.index}].text" value="${answer.text}" class="form-control"/></div>
</div>
<div class="form-group">
<div class="col-sm-6 col-sm-offset-6">
<div class="checkbox">
<label>
<input type="checkbox" name="blocks[${counter.index}].answers[${counterInner.index}].right" value="true" ${answer.checked()}/>
<s:message code="blocks.right"/>
</label>
</div>
</div>
</div>
</fieldset>
</div>
</c:forEach>
</div>

Spring form:errors doesn't render

I have fiollowing controller method:
#RequestMapping(value = "/admin/addNewAdmin")
public String adminUsers(#ModelAttribute #Valid TerminalAdmin newAdmin, BindingResult bindingResult, ModelMap model, Principal principal, HttpSession session) {
if (bindingResult.hasErrors()) {
...
model.addAttribute("newAdmin",newAdmin);
return "admin/adminUsers";
}
....
}
and following model:
Entity
#Table(name = "terminal_admin")
public class TerminalAdmin {
.....
#Column(name = "email", nullable = false)
#Size(max = 255)
#NotEmpty
private String email;
}
jsp:
<form:form modelAttribute="newAdmin" action="/admin/addNewAdmin">
<div class="line">
<label for="">email</label>
<form:input path="email" type="text" name="emal" value=""/>
!!!<form:errors path="email" cssClass="error"/>!!!
<div class="clear"></div>
</div>
</form:form>
I input empty email into form
In debug in controller method I see that bindingResult has errors that email shouldn't be empty.
jsp renders as:
<form id="newAdmin" action="/admin/addNewAdmin" method="post">
<div class="line">
<label for="">email</label>
<input id="email" name="email" type="text" value="">
!!!!!!
<div class="clear"></div>
</div>
</form>
Working after replace
newAdmin
with
terminalAdmin
thx second point of this https://stackoverflow.com/a/2912049/2674303

Categories

Resources