I'm trying to create a table of attendees of my event using Thymeleaf on a Spring Boot app.
My table looks like this:
<table class="table table-bordered tabl-striped">
<thead class="thead-dark">
<th>First Name</th>
<th>Last Name</th>
</thead>
<tbody>
<tr th:each="tempEvent : ${event}">
<td th:text="${tempEvent.clubEventAttendees[event].firstName}" />
<td th:text="${tempEvent.clubEventAttendees[event].lastName}" />
</tr>
</tbody>
</table>
If I just use a single td with <td th:text="${tempEvent.clubEventAttendees}" /> I get this:
[EventAttendees [id=1, firstName=Paul, lastName=Jones], EventAttendees [id=2, firstName=Luke, lastName=Smyth]
This output contains the correct data. The above table however only outputs my second attendee(Luke Smyth). If I change the td lines to:
<td th:text="${tempEvent.clubEventAttendees[0].firstName}" />
<td th:text="${tempEvent.clubEventAttendees[0].lastName}" />
or
<td th:text="${tempEvent.clubEventAttendees[1].firstName}" />
<td th:text="${tempEvent.clubEventAttendees[1].lastName}" />
The correct person is displayed depending on their position. How can I change my table to display any attendees that get returned?
This is my Event model:
#Entity
#Table(name="club_events")
public class Event {
#OneToMany(mappedBy="clubEvent",
cascade={CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH,
CascadeType.REMOVE})
private List<EventAttendees> clubEventAttendees;
// define fiends
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#Column(name="location")
private String location;
#Column(name="description")
private String description;
#DateTimeFormat(pattern="yyyy-MM-dd")
#Column(name="date_time")
private LocalDate dateTime;
public Event() {
}
public Event(int id, String name, String location, String description, LocalDate dateTime) {
super();
this.id = id;
this.name = name;
this.location = location;
this.description = description;
this.dateTime = dateTime;
}
}
This is my EventAttendees model:
#Entity
#Table(name="club_event_attendees")
public class EventAttendees {
#ManyToOne(cascade= {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name="club_event_id")
private Event clubEvent;
// define fiends
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="phone")
private String phone;
#Column(name="mobile")
private String mobile;
public EventAttendees() {
}
public EventAttendees(int id, String firstName, String lastName, String phone, String mobile, int clubEventId) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.phone = phone;
this.mobile = mobile;
}
}
Note: getters and setters have been removed.
I currently have a page which lists all events(in a table):
The Attendees column will be removed once I get this figured out but if the View Attendees button is clicked I'd like to be taken to a page containing a similar table but with the firstName, lastName, phone and mobile of all attendees. Basically, I'd like to take the data returned in clubEventAttendees and display it in a table similar to the table above.
Okay so as promised, here is the code.
I can explain the example with Person and Car. The idea is that Person has List<Car>.
After pressing on View Cars, a new page is opened, where you see a list of Cars.
How to achieve this is fairly simple. When rendering List<Person> from modelAttribute people, you can use the following code
<tr th:each="person : ${people}">
<td th:text="${person.id}"></td>
<td th:text="${person.firstName}"></td>
<td th:text="${person.lastName}"></td>
<td>
<a th:href="#{'/person/' + ${person.id} + '/cars'}">View Cars</a>
</td>
</tr>
Href from th:href="#{'/person/' + ${person.id} + '/cars'}" executed following method in controller
#GetMapping("/person/{personId}/cars")
public String getCars(Model model, #PathVariable Integer personId){
model.addAttribute("cars", carRepository.findAllByPersonId(personId));
return "web/car-list";
}
after which a new template is rendered. I uploaded source code on GitHub, it can be found here https://github.com/Prebiusta/thymeleaf-playground
Hope it helps, let me know if there is something else.
Related
I am writing a spring boot application to perform certain jobs on a MySQL database. One of the things I am having trouble with is getting the average temperature per day from the database.
My #entity looks as follows:
#Entity
public class SensorData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long sensorDataId;
#Column(
name = "timestamp"
)
private LocalDateTime timestamp;
#Column(
name = "temperature"
)
private Float temperature;
#Column(
name = "humidity"
)
private Float humidity;
#ManyToOne(
fetch = FetchType.LAZY,
optional = false,
cascade = CascadeType.ALL
)
#JoinColumn(
name = "department_id",
nullable = false
)
private Department department;
}
And department entity as follows. I am working with department (1)"A", (2)"B" and (3)"C".
#Entity
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long departmentId;
#Column(
name = "department",
unique = true
)
private String department;
#OneToMany(
mappedBy = "department",
fetch = FetchType.LAZY
)
private List<SensorData> sensorData;
}
The problem I am facing is that I need to make a query that returns average temperature per day per department. In my attempts to do this I have written the following JpaRepository function:
#Query(value = "select round((dc.temperature),3) as temperature
,dc.department_id,dc.timestamp from db_uppg.sensor_data dc group by department_id,dc.timestamp",nativeQuery = true)
List<?> findTemperature();
And a controller function that returns a thymeleaf template averageTemperature.html:
#Controller
public class IndexController {
#Autowired
private SensorDataRepository sensorDataRepository;
#GetMapping("/averageHumidity")
public String avgHumidity(Model model) {
List<?> list = sensorDataRepository.findHumidity();
model.addAttribute("allHumidity", list);
return "averageHumidity";
}
}
And finally my thymeleaf template:
<table class="table table-dark table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Average Temperature</th>
<th scope="col">Date</th>
</tr>
</thead>
<tbody>
<tr th:each="humidityList : ${allHumidity}">
<th scope="row">-</th>
<td th:text="${humidityList.humidity}"></td>
<td th:text="${humidityList.datetime}"></td>
</tr>
</tbody>
</table>
My IDE warns me that it cannot resolve humidity and datetime in my thymeleaf template. I assume it's because I am using List<?>, and Java cannot understand the data this list contains?
I am not sure what other ways I can achieve displaying a result where the columns requested do not match any of my original entities.
That query returns List<Object[]> as the actual type, so use that instead of ?. Then in your service layer (or the controller if you don't have a service layer), convert the rows to suitable DTOs with those fields (humidity and datetime).
The resulting code could look something like
List<HumidityDTO> list = sensorDataRepository.findHumidity().stream()
.map(h -> new (HumidityDTO(h)))
.collect(Collectors.toList());
If HumidityDTO has a constructor taking an Object[] parameter.
How can I populate a dropdown list based on selections of other dropdown lists?
I have a Unit class, a Size class and a City class. The user must first select a country from a dropdown list of countries, the municipalities list will then display only the municipalities in that country, after that the user must select a city size, and at the end of it all, the user must select a city from a list of cities which are of the selected size, and belong to the selected municipality and country.
My code:
Unit.java
public class Unit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable=false)
private String name
#ManyToOne
#JoinColumn
#ToString.Exclude
private UnitType unitType;
#OneToOne
#JoinColumn
private Unit unit;
#OneToMany(mappedBy = "unit", cascade = CascadeType.ALL)
private Set<City> cities;
}
UnitType.java
public class UnitType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable=false)
#Enumerated(EnumType.STRING)
private UnitName uName;
#OneToMany(mappedBy = "unitType", cascade = CascadeType.ALL)
private Set<Unit> units;
public enum UnitName {
COUNTY, MUNICIPALITY
}
}
CitySize.java
public class CitySize {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="naziv", nullable=false)
#Enumerated(EnumType.STRING)
private Size name;
#OneToMany(mappedBy = "citySize", cascade = CascadeType.ALL)
private Set<City> sizes;
public enum Size {
SMALL, MEDIUM, LARGE
}
}
City.java
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable=false)
private String name;
#ManyToOne
#JoinColumn
#ToString.Exclude
private CitySize citySize;
#ManyToOne
#JoinColumn
#ToString.Exclude
private Unit unit;
#OneToMany(mappedBy = "city", cascade = CascadeType.ALL)
private Set<Event> events;
}
Event.java
public class Event {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable=false)
private String name;
#Column(nullable=false)
private LocalDateTime time;
#ManyToOne
#JoinColumn
#ToString.Exclude
private City city;
}
EventController.java
public class EventController {
....
#GetMapping("/citySearch")
public String citySearch(Model model) {
model.addAttribute("event", new Event());
model.addAttribute("unit", new Unit());
model.addAttribute("citySize", new CitySize());
model.addAttribute("counties", unitRepository.findByUnitTypeId(50001L));
model.addAttribute("municipalities", unitRepository.findByUnitTypeId(50002L));
model.addAttribute("sizes", CitySize.Size.values());
model.addAttribute("cities", cityRepository.findAll());
return "citySearch";
}
#PostMapping("/citySearch")
public String citySearch(Event event, Model model, City city, Unit unit,
CitySize citySize) {
List<Event> foundEvents = eventRepository.findByCity(city);
model.addAttribute("unit", new Unit());
model.addAttribute("citySize", new CitySize());
model.addAttribute("counties", unitRepository.findByUnitTypeId(50001L));
model.addAttribute("municipalities", unitRepository.findByUnitTypeId(50002L));
model.addAttribute("sizes", CitySize.Size.values());
model.addAttribute("cities", cityRepository.findAll());
model.addAttribute("foundEvents", foundEvents);
return "citySearch";
}
}
citySearch.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" th:href="#{/css/style.css}" >
<title>City search</title>
</head>
<body>
<h1>Event search by city</h1>
<form th:object="${unit}" method="post">
<div class="form-group">
<label for="unit">County: </label>
<select th:id="countyOption" th:field="*{unit}">
<option value="" >choose counties</option>
<option th:each="county : ${counties}" th:value="${county.id}" th:text="${county.name}"></option>
</select>
</div>
<div class="form-group">
<label for="unit">Municipality: </label>
<select th:id="municipalityOption" th:field="*{unit}">
<option value="" >choose municipilaties</option>
<option th:each="municipality : ${municipilaties}" th:value="${municipality.id}" th:text="${municipality.name}"></option>
</select>
</div>
</form>
<form th:object="${citySize}" method="post">
<div class="form-group">
<label for="name">City size: </label>
<select th:field="*{name}">
<option value="" >choose a city size</option>
<option th:each="name : ${sizes}" th:value="${id}" th:text="${name}"></option>
</select>
</div>
</form>
<form th:object="${event}" method="post">
<div class="form-group">
<label for="city">City: </label>
<select th:field="*{city}">
<option value="" >choose cities</option>
<option th:each="city : ${cities}" th:value="${city.id}" th:text="${city.name}"></option>
</select>
</div>
<input type="submit" th:value="Search">
</form>
<table>
<tr>
<th>Name</th>
<th>City</th>
<th>Time</th>
</tr>
<tr th:each="event : ${foundEvents}">
<td><span th:text="${event.name}" >EVENT.NAME</span></td>
<td><span th:text="${event.city.name}" >CITY.NAME</span></td>
<td><span th:text="${#temporals.format(event.time, 'dd.MM.yyyy. HH:mm')}" >EVENT.TIME</span></td>
</tr>
</table>
<p><a th:href="#{/search}">Return</a></p>
</body>
</html>
So far, my web search provided information that this can't be done by using only Spring boot and Thymeleaf, only with jQuery. Since I don't know jQuery, I would require some instructions on how to write and implement the method in jQuery. Also, I don't have a WebConfig.java class, since I had no need for it so far in my app, but if I need it now, what does it have to contain?
Spring Boot and Thymeleaf can't do anything client-side, but you can still achieve what you are trying to do without Javascript / JQuery:
You can submit the selected country to the backend, where you calculate the possible municipalities for that country, which you can add to the model and only display those (Or disable all other options). However, you will have to do that for every step that limits the options, that means reloading the page everytime, which can be painful to use.
If you want to accomplish the same without reloading the page, you will have to resort to some client-side code - that means Javascript / JQuery.
I have a OneToMany mapping set up for my student to address, so a student may have multiple addresses. The items are mapped correctly, but when I persist data through the form, the id is generated for the address(user is already created I just have to click add address, also I am getting the correct user) but the values are null when I save, here are my java and jsp snippets
#PostMapping("/addAddress")
public String showFormForAddAddress(#RequestParam("studentId") int id, Model model) {
Student student = studentService.findStudentById(id);
Address address = new Address();
model.addAttribute("address", address);
List<Address> addresses = student.getAddresses();
addresses.add(address);
student.setAddresses(addresses);
studentService.saveStudent(student);
return "address-form";
}
#GetMapping("/addAddress")
public String addAddress(#RequestParam("studentId") int id, Model model) {
Address address = new Address();
address.setStudentId(id);
model.addAttribute("address", address);
return "address-form";
}
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Address Form</title>
</head>
<body>
<div id="wrapper">
<div id="header">
<h2>SRM Student Relationship Manager (Address)</h2>
</div>
</div>
<div id="container">
<h3>Save Address</h3>
<form:form action="addAddress" modelAttribute="address" method="POST">
<form:input path="studentId" />
<table>
<tbody>
<tr>
<td><label>Address One</label></td>
<td><form:input path="addressOne" /></td>
</tr>
<tr>
<td><label>City</label></td>
<td><form:input path="city" /></td>
</tr>
<tr>
<td><label>State</label></td>
<td><form:input path="state" /></td>
</tr>
<tr>
<td><label>Zip Code</label></td>
<td><form:input path="zipCode" /></td>
</tr>
<tr>
<td><label>Address Two</label></td>
<td><form:input path="addressTwo" /></td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save"></td>
</tr>
</tbody>
</table>
</form:form>
</div>
</body>
</html>
Also for reassurance case these are my entities
#Entity
#Table(name = "address", catalog = "user_db")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "address_id")
private Integer id;
private String addressOne;
private String city;
private String state;
private String zipCode;
private String addressTwo;
#Column(name = "user_id")
private Integer studentId;
#Entity
#Table(name = "Student", catalog = "user_db")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "student_id")
private int id;
private String firstName;
private String lastName;
private String email;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "student_id", referencedColumnName = "student_id")
private List<Address> addresses;
I feel like this may be a naming issue but I cant quite put my finger on it, also I tried deleting the post and just using get, still didn't work.
this should be the showForForAddMethod
#PostMapping("/addAddress")
public String showFormForAddAddress(#RequestParam("studentId") int id, Model model,
#ModelAttribute("address") Address address) {
Student student = studentService.findStudentById(id);
model.addAttribute("address", address);
List<Address> addresses = student.getAddresses();
addresses.add(address);
student.setAddresses(addresses);
studentService.saveStudent(student);
return "redirect:/student/list";
}
If I create a new object the information is not mapped and hence it is lost, but when I pass address as a model attribute in the parameters, this points to the student object
In your showFormForAddAddress method, you wrote model.addAttribute("address", address); this puts values into model attribute.
But you were trying to get address information from this method, it is wrong.
You can get the related information by using the below approach:
Map<String,Object> modelMap = model.getModel().asMap();
Object object = modelMap.get("address");
After that you can convert the resulted object into Address type and use it
If we are creating a backend for website. We will obviously create categories and posts/products. To add/edit/delete categories we will obviously create a table like structure. for example:This is category listing as table
Category would have a child category in a same table, number of child category could be 1 - many. I would like to show the number of child category as i have mentioned in above image.
As a CI developer i use to do that by using Query in the view,
Here is the example
<?php
if(sizeof($results)>0)
{
$i=1;
foreach($results as $key => $list)
{
if($i%2==0)$cls="row1"; else $cls="row2";
?>
<tr id="<?php echo $list->id;?>" class="<?php echo $cls; ?>">
<td> <?php echo $list->title, anchor("admin/categories/update/".$list->id."/", '<i class="fa fa-pencil rtrt"></i>' ); ?></td>
<td>
<?php
// it will return number of childs
echo anchor ('admin/categories/category/'.$list->id, $this->Common->select_child ('tbl_category', $list->id) );
?>
</td>
Right now i am using Spring-mvc, hibernate, Jpa, and Mysql DB. I am known that doing sql query from a jsp would be a bad practice.
Here is my Jsp Code for table like structure,
<table class="table table-striped">
<thead>
<tr>
<th><i class="fa fa-pencil"></i></th>
<th>Category Name</th>
<th>Child</th>
<th>Created</th>
<th>Updated</th>
<th><input type="checkbox" /></th>
</tr>
</thead>
<tbody>
<c:if test="${not empty category}">
<c:forEach var="cat" items="${category}">
<tr>
<td><i class="fa fa-pencil"></i></td>
<td>${cat.title}</td>
<td><i class="fa fa-folder-open"></i></td>
<td>${cat.created}</td>
<td>${cat.updated}</td>
<td><input type="checkbox" /></td>
</tr>
</c:forEach>
</c:if>
Here is the category POjo class
#Entity
#Table(name = "categories")
public class Categories {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private Long parent_id;
private String title;
private String url_title;
private String description;
private String created;
private String updated;
/*
* # At the time of Creating new user
* Auto persist created date
* Auto persist updated for first time
*/
#PrePersist
protected void onCreate (){
// Date conversion to string
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd h:mm:s");
sdf.setLenient(false);
String now = sdf.format(date);
created = now;
updated = now;
}
/*
* Respective Getter and Setter Methods
*
*/
public Long getId() {
return id;
}public void setId(Long id) {
this.id = id;
}
public Long getParent_id() {
return parent_id;
}public void setParent_id(Long parent_id) {
this.parent_id = parent_id;
}
public String getTitle() {
return title;
}public void setTitle(String title) {
this.title = title;
}
public String getUrl_title() {
return url_title;
}public void setUrl_title(String url_title) {
this.url_title = url_title;
}
public String getCreated() {
return created;
}public void setCreated(String created) {
this.created = created;
}
public String getUpdated() {
return updated;
}public void setUpdated(String updated) {
this.updated = updated;
}
public String getDescription() {
return description;
}public void setDescription(String description) {
this.description = description;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Categories other = (Categories) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
The Question is
how can i get children count without doing sql query from Jsp ?
Please Help me i need a suggestion.
to get a List count on jsp side use jstl functions like below :
declare the taglibe of jstl.functions
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
then you can try this
<c:set var="count" value="${fn:length(you_array)}" />
this will create a variable "count" taking as value : you_array.size()
Hope it will help someone like me,
Modified entity class
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private Long parent_id;
private String title;
private String url_title;
private String description;
private Long status;
private String created;
private String updated;
#ManyToOne
#JoinColumn(
name="parent_id",
insertable=false,
updatable=false)
#NotFound(action = NotFoundAction.IGNORE)
private Categories parentCategory;
#OneToMany(
fetch = FetchType.EAGER,
mappedBy="parentCategory",
cascade=CascadeType.REMOVE,
orphanRemoval=true )
private Set<Categories> child = new HashSet<Categories>();
Since i am new to Jpa/orm/hibernate/spring when i posted this question i did not knew about entity relationship which we can manage in hibernate. I did some research and found out about hibernate 'Uni/Bi directional' association using annotation like (one to many, many to one, many to many). I suggest if you are new to JPA and hibernate then please visit This link before wasting your time.
What I have done
Bidirectional associations with join : using #ManyToOne #Onetomany #joinColumn
Solve Notfound exception using #NotFound(NotFoundAction.IGNORE)
I have a categories (eg. Base category) which wont have any parent category so its 'parent_id' will be 0 in database but Hibernate will throw exception while querying records from database with id 0, after a successfull association.
Used insertable/updatable = false : set this properties to false if you are inserting and updating only one category at a time.
Used "cascade=cascadetype.remove" "orphanremoval=true" : set this properties, if you want to delete child categories when you delete parent category. When you invoke your serviceImplementation.delete(category cat) method, if "cat:object" is parent for other categories hibernate will automatically delete it's children categories before deleting cat:object itself.
At last the Answer for my question
After the successful association with annotation, when you will query category rows from database. If it's a parent category for other categories hibernate will add "child Collection" in your object like this.
03:20:16.490 [http-bio-8080-exec-5] DEBUG
o.h.internal.util.EntityPrinter -
com.pack.librarymanagementsystem.model.Categories
{created=2016-06-10 7:45:32, parent_id=38, url_title=Bhm 5th sem,
description=<p>asd</p>,
parentCategory=com.pack.librarymanagementsystem.model.Categories#38,
id=43, title=Bhm 5th sem, updated=2016-06-10 7:45:32, child=[], status=1}
Check It in your console log if you are using eclipse. The child collection hibernate just added wont have any data. Hibernate will store only proxys.
The solution is to get the size of a child collection.
If you will do isEmpty() check on that child collection the result will be true. Thats where the "fetch = FetchType.EAGER" Kicks in. By default fetchtype is set to lazy. After changing fetchtype to eager hibernate will add objects as a child instead of adding proxy.
If you will do isEmpty check on that child collection now, the result will be false. Which means we can get the size of a collection.
This is the jsp
<c:if test="${not empty category}">
<c:forEach var="cat" items="${category}">
<tr>
<td><i class="fa fa-pencil"></i></td>
<td>${cat.title}</td>
<td>
// Child count
<c:if test="${not empty cat.child}" >
<a href="${pageContext.request.contextPath}/category/show/${cat.id}">
<i class="fa fa-folder-open"></i> ${ fn:length(cat.child)}
</a>
</c:if>
<c:if test="${empty cat.child}" >
<i class="fa fa-folder"></i> 0
</c:if>
</td>
<td>${cat.created}</td>
<td>${cat.updated}</td>
<td>
<c:choose>
<c:when test="${cat.status == 1}">Published</c:when>
<c:otherwise>Unpublished</c:otherwise>
</c:choose>
</td>
<td><input type="checkbox" name="ids[]" id="ids[]" value="${cat.id}" /></td>
</tr>
</c:forEach>
And this is the controller
#RequestMapping(value = "/show/", method = RequestMethod.GET)
public String viewCategory( Map<String, Object> model) {
List<Categories> category = categoryService.getAllCategoriesByParent(Id);
model.put("category",category);
return "category/view";
}
I hope this would help someone on saving his/her time.
Thank you
I'm new in Spring and I have a problem with form validation. In my user edit form I want to validate only specific fields, not all fields annotated in my entity.
For example, if I have my UserEntity with fields:
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#Column(name = "user_id")
#GeneratedValue
public int user_id;
#NotEmpty
#Column(name = "userlogin")
public String userlogin;
#NotEmpty
#Column(name = "userpass")
public String userpass;
#NotEmpty
#Column(name = "name")
public String name;
#Email
#NotEmpty
#Column(name = "email")
public String email;
#NumberFormat(style = Style.NUMBER)
#NotEmpty
#Column(name = "phone")
public String phone;
and when I have register form, I need to validate all fields, and that's working fine.
But when I have edit user form, I want to edit and validate only 'name', 'email' and 'phone', I don't want to change 'userlogin' and 'userpass'.
But edit form won't pass successfully, because BindingResult validating all fields.
Here's my edit form:
<springForm:form action="/mywebapp/user/edit" commandName="user" method="POST">
<table>
<tr>
<td>Name:</td>
<td><springForm:input path="name" value="${user.name}" /></td>
<td><springForm:errors path="name" cssClass="error" /></td>
</tr>
<tr>
<td>E-mail:</td>
<td><springForm:input path="email" value="${user.email }" /></td>
<td><springForm:errors path="email" cssClass="error" /></td>
</tr>
<tr>
<td>Phone:</td>
<td><springForm:input path="phone" value="${user.phone}" /></td>
<td><springForm:errors path="phone" cssClass="error" /></td>
</tr>
<tr>
<td><input type="submit" value="Edit" /></td>
</tr>
</table>
</springForm:form>
Here is my controller method:
#RequestMapping(value="user/edit", method=RequestMethod.POST)
public String doUserEdit(#Valid #ModelAttribute("user") UserEntity user, BindingResult result, Principal principal) {
if(result.hasErrors()) {
return "user/edit";
}
UserEntity u = this.userService.getUser(principal.getName());
this.userService.editUser(u.getUser_id(), user);
return "redirect:/user";
}
result.hasError() always return true, because it validating also 'userlogin' and 'userpass'.
How to ignore other fields in edit form and validate only that fields that I want to?
I usually create a separate form class which is only suited for form submission processing and put all the necessary validation there:
public class UserUpdateForm {
#NotEmpty
private String name;
#Email
private String email;
#NotEmpty
#NumberFormat(style = Style.NUMBER)
private String phone;
//Getters and setters here
}
The idea is that you untie your model class from representations (form) classes. The only downside to that is that you'll have to handle transformations between the form objects and model objects somehow. Something like dozer might help though.