Thymeleaf form multi select - java

I hope you can help me with a problem I have.
I need to create a form where I can create a CourseForm object that has a list of courses with id and name.
From the create button I pass a list of available courses and the object where I want the courses selected in the form to be stored.
In turn, I have a multi select in the form that uses the CourseForm object and its courses attribute, which is a list of courses.
The list is printed correctly in the view but when retrieving the selected courses it returns a null value
Create Controller
model.addAttribute("courseForm", courseForm);
model.addAttribute("coursesList", coursesList);
Form
<form th:action="#{/courses/save}" th:object="${courseForm}" method="POST">
<select class="selectpicker" th:field="*{courses}" multiple="multiple">
<option th:each="course: ${coursesList}"
th:value="${course.courseId}" th:text="${course.courseName}">
</select>
CourseForm entity
#Data
public class CourseForm {
private List<CourseIndividual> courses;
}
CourseIndividual entity
#Data
public class CourseIndividual {
private Integer courseId;
private String courseName;
}
Save Controller
#PostMapping("/courses/save")
public ModelAndView saveAnnouncementCourses(
Model model,
#RequestParam("save_option") String saveOption,
#ModelAttribute("courseForm") CourseForm courseForm

Related

Thymeleaf Option Without Selection

From the controller, I have returned an object containing a list of objects. I want to display these list of objects in the dropdown with no pre-selection(or default value i.e. "Select Dish"), but dropdown is shown with pre-selected last value in the list.
Controller:
#GetMapping(path = "/createOrder")
public ModelAndView displayOrder(OrderFormDetails order) {
ModelAndView mav = new ModelAndView();
mav.addObject("order", orderService.displayOrder());
mav.setViewName("createOrder");
return mav;
}
Model:
public class OrderFormDetails {
#NotEmpty(message = "*Please provide your name")
private String name;
#NotEmpty(message = "*Please provide your address")
private String address;
private List < Dish > dishes;
View:
<select class="form-control" th:field="*{dishes}" id="dropOperator">
<option value="" selected="selected">Sélect dish</option>
<option th:each="dish, itemStat : *{dishes}" th:value="*{dishes[__${itemStat.index}__].id}" th:text="*{dishes[__${itemStat.index}__].title}">
</option>
</select>
I have tried multiple tricks, but none of them worked. Thanks...
Usually you don't want to mix possible options and selected option together within one field (as you apparently did). All you need to do is to decouple those things. Possible steps may be helpful:
Since OrderFormDetails acts like your form backing bean, it should contain a placeholder for selected value (Dish.id) instead of List with possible dishes. Change your OrderFormDetails to the following:
public class OrderFormDetails {
#NotEmpty(message = "*Please provide your name")
private String name;
#NotEmpty(message = "*Please provide your address")
private String address;
private T selectedDish;
// getters, setters, rest of the class omitted
...where T is the type assigned to Dish.id field.
Possible (selectable) dishes should be provided separately as a model attribute. Add following line to your displayOrder method:
mav.addObject("dishes", getDishes());
...where getDishes() returns List<Dish> containing all dishes being an option for an user.
Adjust your view to work with updated approach:
<select class="form-control" th:field="*{selectedDish}" id="dropOperator">
<option value="" selected="selected">Sélect dish</option>
<option th:each="dish : ${dishes}" th:value="${dish.id}" th:text="${dish.title}">
</option>
</select>
That's all. Such an approach is also shown in documentation - th:field on <select> level refers to form backing bean's field, whereas <option> elements are created out of separate collection provided as model attribute.

How to define a POJO to hold list of data from submit form?

I want to insert list of items into a database table submitted from multiple select form.
My submit form is:
<form method="post" action="${pageContext.request.contextPath }/">
<div>
<label>User:</label>
<select name="customer">
<option value="">Select Customer</option>
<c:forEach var="c" items="${ cList }">
<option value="${ c.id }">${ c.name }</option>
</c:forEach>
</select>
</div><br>
<div>
<label>Hobby:</label>
<select name="product" multiple size="8">
<!-- <option value="">Select Items</option> -->
<c:forEach var="p" items="${ pList }">
<option value = "${ p.id }">${ p.productName }</option>
</c:forEach>
</select>
</div>
<br><br>
<input type="submit" value="Send">
</form>
And so far i have done this:
My Transaction POJO:
#Entity
#Table(name = "transcation")
public class Transcation {
#Id
#GeneratedValue
#Column
private Long id;
#JoinColumn
#OneToOne
private Customer customer;
#JoinColumn
#OneToMany
private List<Product> product;
...
My Product POJO:
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue
#Column
private Long id;
#Column
private String productName;
...
But when i run this project i get this type of database table for transcation and product:
Product table in database
Transaction table in database
I need to insert the customers' transaction with the list of items and customer id in the transaction table.
I assume the database tables are generated from the Entity you defined. Hence its okay to have such tables generated. But the thing is you are interpreting a wrong relationship between Tranasaction and Product.
Think - Is it really an OneToMany relation ? I guess No. Cause not only an Transaction contains list of Product but also a Product could be in multiple Tracnsaction. So this should be an unidirectional ManyToMany relation.
If I understand your business this okay then just annotate you Transaction Entity like below (having #JoinTable for the ManyToMany relation.
#Entity
#Table(name = "transcation")
public class Transcation {
#Id
#GeneratedValue
private Long id;
#OneToOne
#JoinColumn(name="customer_id")
private Customer customer;
#ManyToMany
#JoinTable(name = "transaction_product",
joinColumns = #JoinColumn(name = "transaction_id"),
inverseJoinColumns = #JoinColumn(name = "product_id"))
private List<Product> productList;
...
Having #JoinTable actually creates separate mapping table tranasction_product in the database having foreign key reference with transaction and product table primary key.
Saving this Relation from UI
Assuming you have the selected Customer and the selected List of Product from the submission. Now just create a Transaction. Simply assign the values and save your Tranasction object through an opened session
Transaction transaction = new Transaction();
transaction.setCustomer(customer);
transaction.setProductList(productList);
Your Entities seems to be correct.
First please add products into database. Consider you have added products already. id of these are 1,2,3.
Before persisting transaction load products by ids and add into product list.
Then create object of transaction :
Transaction = new Transaction();
transaction.setProducts(products); //list of products loaded already into list
Then set other parameters and finally just save this transaction.
You want to map an collection type (List) in Transaction table. You should try to annotate the list with #ElementCollection annotation like below:
...
#JoinColumn
#OneToMany
#ElementCollection
private List<Product> product;
Of course we take for granted that Product table actually exists.

Need to add unknown number of student records to a school record

I have a class called school which has a list of students. I need to have a registration page to register a school and all its students. As number of students for each school is unknown, I need to have a 'add student' button in the 'school registration' page to allow users to add all students one by one. I can create a form for registering each school, but am not sure how to implement the 'add students' functionality.
I know it is possible to have a 'add student' button which trigger a lightbox of student registration form, but I do not know how to keep details of each student and then associate them to the school record; because student records need id of school vice versa. Please note I do not want to submit school record and then add the students records, I need to add details of school and all its students at the same time.
#Entity
public class School {
#Id
#GeneratedValue
private long id;
private String name;
private String principal;
private String city;
#OneToMany
private List<Student> students;
....
}
#Entity
public class Student {
#Id
#GeneratedValue
private long id;
private String name;
private int age;
#OneToOne
private School school;
....
}
Form
<s:form method="POST" action="register">
<s:textfield name="name" label="School's name"/>
<s:textfield name="principal" label="Principal"/>
<s:textfield name="city" label="City"/>
</s:form>
So, if I'm understanding the problem correctly, you have a page to create a school entity and after the school is created, you want it to then proceed to a page to register each student.
Zero is a perfectly valid size for the list of students so you should have no problem creating the school without a single student. Make sure and save the changes to the DB.
The "add a student" page will need to be passed the ID of the school in order have that information to associate the student to the correct school. (in the url or as a query string http://example.com/register/student?school=23) Another way to do that would be to have a dropdown box of possible schools and let them select their school out of the existing schools.
When you create a student you'll assign their school ID appropriately and add the student the school's list of students. Then save the changes to the DB again with the modifications.

Spring MVC + Hibernate: How to handle form with relation

I have problem with adding/updating records with relations. Could please some advice how it should work?
I have two entities: Question and Category:
public class Question {
#Id
#GeneratedValue
private Long questionId;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category")
private Category category;
and
#Entity
#Table(name = "category")
public class Category {
#Id
#GeneratedValue
private Long categoryId;
private String name;
I have some list of categories and I would like to add new Question with selected Category. So in my QuestionController I have add method:
#RequestMapping(value = "/add", method = RequestMethod.GET)
public ModelAndView add() {
ModelAndView mav = new ModelAndView("question/add");
mav.addObject("question", new Question());
mav.addObject("categoryList", categoryService.getAll());
return mav;
}
and form:
<form:form modelAttribute="question" method="POST" >
Name: <form:input path="name" value="${ques.name}" />
Category: <form:select path="category" items="${categoryList}" />
<input type="submit" value="Add" />
</form:form>
Everything looks good for now (I can fill question name and select category). But I don't know how add POST method should work
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String added(#ModelAttribute Question question, BindingResult bindingResult) {
}
When I try to use above method I have error: Failed to convert property value of type 'java.lang.String' to required type model.Category
I've tried to look for similar problem but I coudln't find anything.. So if someone can help/advice or show link to similar issue I would be grateful!
Cheers!
You need to provide code for Spring that tells it how to convert the string value from the web page back into a Category object. This is done by either:
Adding a PropertyEditor to the DataBinder.
Creating a Converter.
It is a bad practice using hibernate objects to map the form items. There are two solutions
Add another property private transient String categoryString; to the 'Question' class. and map the UI category to this <form:select path="categoryString" items="${categoryList}" />
That way you can avoid the error.
Do not use the hibernate mapping classes for mapping the form items, use POJOs for doing this. and later somewhere in your application map this simple pojo elements onto the hibernate entity.
try changing this line:
<form:select path="category" items="${categoryList}" />
to:
<form:select path="category.categoryId" items="${categoryList}" itemLabel="name" itemValue="categoryId"/>
Then in the added method (post method), retrieve the Category object back from hibernate and set back on question object before saving:
Category selectedCategory = yourHibernateService.getCategoryById(question.getCategory().getCategoryId());
question.setCategory(selectedCategory);

JSF2: selectOneMenu with custom selectItems: all items are selected

Thanks to some great articles here, I've been able to build a <h:selectOneMenu /> with selectItems containing objects. After providing a custom FacesConverter and fixing the missing equals()/hashcode() methods, I am able to use it to change the property of the backing bean and write it out to the DB.
The only weird thing is that all HTML <option /> elements of the <select />-element are checked="checked"! In other words: the <h:selectOneMenu /> does not reflect the current value of the bound property!
Details:
Store.java
#Entity
public class Store {
private Long id;
private String name;
#ManyToOne
private Category category;
// getters, setters, equals, hashcode
}
Category.java
#Entity
public class Category {
private Long id;
private String name;
// getters, setters, equals, hashcode
}
editStore.xhtml
<h:form>
....
<h:selectOneMenu value="#{backingBean.store.category}" id="category">
<f:selectItems value="#{backingBean.categorySelectItems}" />
</h:selectOneMenu>
....
</h:form>
BackingBean.java
public class BackingBean {
private Store store;
// inject data-access-facades via #EJB
// Constructor
// getters, setters
public List<SelectItem> getCategorySelectItems
List<SelectItem> items = new ArrayList<SelectItem>();
for (Category cat : categoryFacade.findAll() ) {
items.add(new SelectItem(cat, cat.getName()));
}
return items;
}
// action methods
}
I leave out listing the Category-Converter (it converts between the object and its ID).
The HTML this creates is:
<select id="category" name="category" size="1">
<option value="251" selected="selected">Kosmetik</option>
<option value="222" selected="selected">Sportwaren</option>
</select>
Obviously, store.category can only contain one item... why are both option-elements "selected"? No matter, what category is assigned to the store, the HTML always "selects" all option-elements.
How does JSF now, which SelectItem should be selected?
It's almost certain that the problem is in the equals(..) method, which returns true for all compared objects. Test this, and let your IDE generate the method (together with hashCode())

Categories