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())
Related
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
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.
Considering the following:
public class Company {
private String name;
private List<Person> employees;
//getters and setters
}
public class Person {
private String name;
private List<Car> cars;
//getters and setters
}
public class Car {
private String color;
//getter and setter
}
I want to get with Struts2 the color property of every car for every employee. So how would I iterate over the list of cars of every employee of the Company object?
After creating it, I put employees as employeeList into session. Using the following method, I can only get the name property, but can't iterate over the cars property:
<s:if test="#session.employeeList.size() > 0">
<s:iterator value="#session.employeeList">
<s:property value="name" /> //this works
<s:property value="cars" /> //how do I iterate over this list?
</s:iterator>
</s:if>
Doing this, the output for "cars" property is ognl.NoConversionPossible
I tried something like:
<s:if test="#session.employeeList.size() > 0">
<s:iterator value="#session.employeeList">
<s:property value="name" /> //this works
<s:iterator value="cars">
<s:property value="color" />
</s:iterator>
</s:iterator>
</s:if>
but doesn't work that way. Any ideas?
LE: Solution: Actually the iteration works. The objects I was talking about are mapped as Hibernate Entities. The "List cars" property is in fact a #ManyToMany relationship. So, after looking deeper into the error logs I found out that the cars property that I was trying to iterate was not being populated because Hibernate was lazily initializing it by default. I changed the FetchType to EAGER and the problem was solved.
The fact that I use session over the action attributes is not really important. Though, how could I use action attributes in this case? It doesn't seem doable.
Why not ?
Action:
public class MyAction extends ActionSupport {
private Company company; // private attribute
public Company getCompany(){ // public getter
return company;
}
public String execute(){
company = someService.loadTheCompanySomehow();
return SUCCESS;
}
}
JSP:
<s:iterator value="company.employees">
<s:property value="name" />
<s:iterator value="cars">
<s:property value="color" />
</s:iterator>
</s:iterator>
If the inner iteration doesn't work, there is probably something you have not shown to us, or related to the way you've cleaned the code before posting it.
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);
I am a newbie in spring mvc framework -
I am stuck with a problem which is something like that -
There are classess
class User{
private String name;
private City city;
//getters setters
}
class Country{
private int id;
private String name;
//getters setters
}
class State{
private int id;
private String name;
private Country country;
//getters setters
}
class city{
private int id;
private String name;
private State state;
State state;
//getters setters
}
I have a form like that
<form:form modelAttribute="user" method="POST">
<form:label path="name">Name</form:label>
<form:input path="name"/>
<form:label path="city.state.country">Country</form:label> //Getting error here
<form:select path="city.state.country"></form:select>
<form:label path="city.state"></form:label>
<form:select path="city.state"></form:select>
<form:label path="city"></form:label>
<form:select path="city"></form:select>
</formform>
Error
org.springframework.beans.NotReadablePropertyException: Invalid property 'city.state' of bean class [org.opentutor.beans.User]: Bean property 'city.state' is not readable or has an invalid getter method:
Does the return type of the getter match the parameter type of the setter?
Basically I am confused how to set users country, state and city values.
It isn't enough for your classes to have getX methods. They need to actually return objects that make sense. For example, getCity() needs to return a City object, so that the path city.state.country can be resolved as user.getCity().getState().getCountry(). It can't do that if you have a getCity() method that returns an int.
In User you need a getCity() method. In City you need a getState() method, in State you need a getCountry() method. You also need to preinitialize the different objects. If one of the elements in the path resolves to null it will not work (as there is nothing to bind to).
Basically you need an empty, non-null, object for each of the elements in the path. Depending on the Spring version you use this is automatically handled for you (in older versions you needed to manually initialize the whole path).