Spring form:errors doesn't render - java

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

Related

Registration page and post request

I want to create my own registration page with spring boot, instead of sending a JSON POST request from Postman.
I currently have an HTML page with a form which submits the registration data, a registration controller, and registration service. However, it is not working properly.
My code:
RegistrationRequest
#Getter
#AllArgsConstructor
#EqualsAndHashCode
#ToString
public class RegistrationRequest {
private final String firstName;
private final String lastName;
private final String email;
private final String password;
}
Registration Controller
#RestController
#RequestMapping(path = "api/v1/registration")
#AllArgsConstructor
public class RegistrationController {
private final RegistrationService registrationService;
#PostMapping
public String register(#RequestBody RegistrationRequest request) {
return registrationService.register(request);
}
#GetMapping(path = "confirm")
public String confirm(#RequestParam("token") String token) {
return registrationService.confirmToken(token);
}
HTML form
<form class="form-signin" method="post" action="api/v1/registration">
<h2 class="form-signin-heading">Please Log in to tour agency account</h2>
<p>
<label for="firstName" class="sr-only">First name</label>
<input type="text" id="firstName" name="firstName" class="form-control" placeholder="First name" required="" autofocus="">
</p>
<p>
<label for="lastName" class="sr-only">Last name</label>
<input type="text" id="lastName" name="lastName" class="form-control" placeholder="Last name" required="" autofocus="">
</p>
<p>
<label for="email" class="sr-only">Email</label>
<input type="text" id="email" name="email" class="form-control" placeholder="Email" required="" autofocus="">
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required="">
</p>
<button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
</form>
Template controller
#Controller
#RequestMapping("/")
public class TemplateController {#PostMapping("register") // I know that "register is not linked with path = api/v1/registeration
public String postRegister() {
return "register";
}
}
You need either need a template engine, or you could use Javascript and submit an AJAX request for that. If you want to use a template engine, I recommend using Thymeleaf, however there are many template engines that are available for use.
Using Thymeleaf, you'd have to modify your template to include th:action, which describes your endpoint, and th:object, which is your model. Then we name it as register.html:
<form action="#" th:action="#{/api/v1/registration}" th:object="${registrationRequest}" method="post">
<!-- Form inputs... -->
<button type="submit">Register</button>
</form>
Then in the controller, we need to tell Spring about our HTML page and the model:
#GetMapping("/register")
public String registerationForm(Model model) {
model.addAttribute("registrationRequest", new RegistrationRequest());
return "register";
}
So now when we open /register, our HTML page is loaded.
Finally, modify your POST endpoint to get our form's submitted model:
#PostMapping
public String register(#ModelAttribute RegistrationRequest registrationRequest) {
return registrationService.register(request);
}
This returns the result from registration service, you could also return a string which represents a page name, e.g. return "result", here result is an HTML page result.html.

Null Objects after submitting Form in Spring (H2, SpringBoot, Thymeleaf, CRUD)

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.

Neither BindingResult nor plain target object for bean name 'phonebook' available as request attribute

I trying to add validation to phone number input but i stuck at resolving this error when accessing index.
The error message
Caused by: org.attoparser.ParseException: Error during execution of
processor
'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor'
(template: "index" - line 76, col 73)
Caused by: java.lang.IllegalStateException: Neither BindingResult nor
plain target object for bean name 'phonenumber' available as request
attribute
index.html
<div class="myForm">
<form th:action="#{/save}" method="post" th:object="${phonebook}">
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Update or Create</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">
<input type="hidden" class="form-control" id="id" name="id" value=""/>
</div>
<div class="form-group">
<label for="surname" class="col-form-label">Surname:</label>
<input type="text" class="form-control" id="surname" name="surname" value=""/>
</div>
<div class="form-group">
<label for="firstname" class="col-form-label">First Name:</label>
<input type="text" class="form-control" id="firstname" name="firstname" value=""/>
</div>
<div class="form-group">
<label for="phonenumber" class="col-form-label">Phone Number:</label>
<input type="text" class="form-control" th:field="*{phonenumber}" id="phonenumber" name="phonenumber" value=""/>
<span th:if="${#fields.hasErrors('phonenumber')}" class="help-block" th:errors="*{phonenumber}"></span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<input type="submit" class="btn btn-primary" value="Save"/>
</div>
</div>
</div>
</div>
</form>
Controller
#RequestMapping(value = {"/"}, method = RequestMethod.GET)
public String showPage(Model model, #RequestParam(defaultValue = "0") int page) {
model.addAttribute("data", phonebookRepository.findAll(PageRequest.of(page, 10)));
model.addAttribute("currentPage", page);
return "index";
}
#PostMapping("/save")
public String save(#Valid Phonebook p, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "redirect:/";
}else {
phonebookRepository.save(p);
}
return "redirect:/";
}
Phonebook
#Entity
#Table(name = "Phonebook")
public class Phonebook {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#NotNull
#Size(max=15, message = "Max symbols is 15")
#Column(name = "phonenumber", length = 15, nullable = false)
private String phonenumber;
#Column(name = "surname", length = 50, nullable = false)
private String surname;
#Column(name = "firstname", length = 50, nullable = false)
private String firstname;
//getters and setter
I've got 2 approach to get this issue solve :--
1.) use #RequestParam to get the Phonebook Object like this :--
#PostMapping("/save")
public String save(#Valid #RequestParam("phonenumber")String phonenumber, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "redirect:/";
}else {
phonebookRepository.save(p);
}
return "redirect:/";
}
OR -- Use #ModelAttribute to get the values of your form, like this :--
1.) create a new Phonebook object and add in model attribute :-
#RequestMapping(value = {"/"}, method = RequestMethod.GET)
public String showPage(Model model, #RequestParam(defaultValue = "0") int page) {
//your code
model.addAttribute("phoneBook", new Phonebook());
return "index";
}
2.) Changes in your Thymeleaf / HTML page (use th:object to send your Phonebook object after submit) :--
<form th:action="#{/save}" method="post" th:object="${phoneBook}">
// your code
</form>
3.) Then Use #ModelAttribute to bind the values like this :--
#PostMapping("/save")
public String save(#Valid #ModelAttribute("phoneBook")Phonebook p, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "redirect:/";
}else {
phonebookRepository.save(p);
}
return "redirect:/";
}
4.) And finally your Phonebook class with getter & setter Methods.

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 MVC Validation w/ ModelAndView

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.

Categories

Resources