How to proceed with complex object in POST request - spring

Hello SO I had 2 entities
Main entity
#Entity
#Table(name = "Events")
public class Event {
//Some fields ...
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "event_eventtypes" ,
joinColumns = #JoinColumn(name = "event_id"),
inverseJoinColumns = #JoinColumn(name = "event_type_id"))
private Set<EventType> eventTypes;
//Getters and setters...
Now I have a form that is created using spring form-taglib
<form:form modelAttribute="event" method="post" action="/event/registerEvent" commandName="event">
<form:label path="name">Event display name</form:label>
<form:input path="name" type="text" cssClass="form-control" placeholder="Display name"/>
<form:label path="description" >Description</form:label>
<form:textarea path="description" cssClass="form-control"/>
<form:label path="priv" cssClass="">Make private? <span style="font-family:'Open Sans', sans-serif; font-size:11px; color: dodgerblue;">(It will be seen by your friends and people who you send invitation.)</span></form:label>
<form:checkbox path="priv" cssClass="form-check-input"/>
<form:label path="age">Age limit</form:label>
<form:select path="age">
<form:options items="${age}"/>
</form:select>
<form:hidden path="lng" id="formLang" />
<form:hidden path="lat" id="formLat"/>
<%--Question appear here--%>
<form:select path="eventTypes" items="${eventTypes}" multiple="true"/>
<input type="submit" value="Submit">
Controller
#Controller
public class EventController {
private static final Logger logger = LoggerFactory.getLogger(EventController.class);
#Autowired
private EventService eventService;
#Autowired
private UserService userService;
#Autowired
private EventTypeService eventTypeService;
#RequestMapping(path = "/event/create", method = RequestMethod.GET)
public String init(ModelMap modelMap) {
List<String> tags = eventTypeService.listEventTypes().stream().map(EventType::getName).collect(Collectors.toList());
ArrayList<Integer> ageArr = new ArrayList();
ageArr.add(0);
ageArr.add(6);
ageArr.add(12);
ageArr.add(16);
ageArr.add(18);
modelMap.addAttribute("event", new Event());
modelMap.addAttribute("age", ageArr);
modelMap.addAttribute("eventTypes", tags);
return "/event/create";
}
#RequestMapping(path = "/event/registerEvent", method = RequestMethod.POST)
public String createEvent(#ModelAttribute("event") Event event, #ModelAttribute("eventTypes1") List<String> eventTypes){
event.setDate(new Date());
event.setUser(userService.getUserByUsername(
AuthenticationService.getLoggedInUser())
);
eventService.addEvent(event);
return "redirect:/";
}
When complete form with values, click submit, get error
400 The request sent by the client was syntactically incorrect.
It's because eventTypes property is of String type. How can I send that list as another parameter in controller or what should I do it?

Maybe you need a DTO to include event and eventType list both.

When I have done this I send a DTO with a list of IDs/Strings for event types to the form and then create a new event from the DTO.
For the select statements loop round each option returned in the DTO and use a service findById to retrieve each eventType and then add this to the event model.
for (String tag : eventDTO.getEventTags()) {
EventType eventTag = eventTypeService.findById(Long.valueOf(tag));
if (eventTag != null) {
event.getEventTypes().add(eventTag );
}
}

Found soulution.
Just implement jsp-based form as :
<form:form modelAttribute="event" method="post" action="/event/registerEvent" commandName="event">
...
<%--There I'm not using more jsp tabs.--%>
<select multiple name="et" id="tags">
<c:forEach items="${eventTypes}" var="e">
<option value=${e}>${e}</option>
</c:forEach>
</select>
<input type="submit" value="Submit">
</form:form>
In Controller I get parameters as request.getParameterValues("et")) - that return String[] from HttpServletRequest

Related

How to transfer the list of strings in DTO using Thymeleaf

I'm developing Spring + Thymeleaf application. I'm implementing search with multiple params. I have a form with the corresponding DTO. Here is the code of the DTO:
public class ClassSearchDto {
private String searchParam;
private Long programId;
private List<String> teacherNames;
//getters, setters and constructor are omitted
}
As you see, I have a list of strings in my DTO called teacherNames. Here is the way I'm displaying my form:
<form th:action="#{/classes/search}" method="get" th:object="${classSearchDto}">
<div class="form-group">
<input type="hidden" class="form-control"
th:value="${classSearchDto.programId}" th:field="*{programId}"/>
<label for="searchParam">Search</label>
<input type="text" class="form-control" id="searchParam" placeholder="keyword"
th:value="${classSearchDto.searchParam}" th:field="*{searchParam}"/>
<div>
<th:block th:each="name, iter ${classSearchDto.teacherNames}">
<input th:value="${name}" th:field="*{teacherNames[__${iter.index}__]}/>
</th:block>
</div>
</div>
<button class="btn btn-default" type="submit">Find</button>
</form>
I want to implement my search with help of #RequestParam annotation on the back-end. This is my controller:
#RequestMapping(value = "/search")
public String findClassByName(#RequestParam("searchParam") final String searchParam,
#RequestParam("programId") final Long programId,
#RequestParam("teacherNames") final List<String> teacherNames,
final Model model) {
...
}
The problem is that I can't get the list of teacher names in this way. I get this exception:
org.springframework.web.bind.MissingServletRequestParameterException:Required List parameter 'teacherNames' is not present
Could you please help me to transfer the list of elements in DTO to my back-end with this approach? Maybe you know how to do it correctly in another way. Thank you in advance.
I can suggest you one thing, I don't know whether it works or not. Try changing
public String findClassByName(#RequestParam("searchParam") final String searchParam,#RequestParam("programId") final Long programId,#RequestParam("teacherNames") final List<String> teacherNames,final Model model)
to
public String findClassByName(#ModelAttribute("classSearchDto") ClassSearchDto classSearchDto,#RequestParam("searchParam") String searchParam,#RequestParam("programId") Long programId,#RequestParam("teacherNames") List<String> teacherNames,Model model)

Spring MVC bind multi select with view model on post method

How to bind a multi select to a view model on post method?
This is the view model:
public class AssignEvaluationViewModel {
private String evaluationType;
private String milestone;
private List<AssigneesViewModel> optionsList;
//getters and setters
}
public class AssigneesViewModel {
private int evaluatorId;
private int evaluatedId;
private String evaluatorName;
private String evalueatedName;
}
Controller
#RequestMapping(value="addAssignment", method = RequestMethod.GET)
public String addAssignment(Model model){
// load the list of evaluation type
List<DropDownListItem> items = new ArrayList<DropDownListItem>();
items.add(new DropDownListItem("1", "Peer evaluation"));
items.add(new DropDownListItem("2", "Team member evaluation"));
items.add(new DropDownListItem("3", "Team evaluation"));
model.addAttribute("items", items);
// load the list of milestones
List<DropDownListItem> milestones = new ArrayList<DropDownListItem>();
List<MilestoneDTO> dtos = milestoneService.getAll();
for (MilestoneDTO m : dtos) {
milestones.add(new DropDownListItem(String.valueOf(m.getId()), m
.getMilestoneName()));
}
model.addAttribute("milestones", milestones);
model.addAttribute("addAssignment", new AssignEvaluationViewModel());
return "addAssignment";
}
#RequestMapping(value="addAssignment", method = RequestMethod.POST)
public String addAssignmentPOST(#ModelAttribute("addAssignment") AssignEvaluationViewModel viewModel){
//save the assignment
return "redirect:assignEvaluationForms";
}
The problem is in the jsp.
<form:form commandName="" modelAttribute="addAssignment" id="addAssignment">
//..................
<div class="selectAssignees">
<p>Assignees:</p>
<form:select multiple="multiple" class="assigneesOptions" path="optionsList" id="assignees">
</form:select>
</div>
//..............
</form:form>
How do i bind the options added by the user in the select with optionsList from the AssignEvaluationViewModel?
If you want to show options in Spring you can use form:options
<form:form commandName="" modelAttribute="addAssignment" id="addAssignment">
<div class="selectAssignees">
<p>Assignees:</p>
<form:select multiple="multiple" class="assigneesOptions" path="optionsList" id="assignees">
<form:option value="NONE" label="--- Select ---"/>
<form:options items="${optionsList}" />
</form:select>
</div>
</form:form>

request sent by the client was syntactically incorrect

I have 2 very similar web functions that use spring framework, Function 1 hits "request sent by the client was syntactically incorrect" while Function 2 works well. I show the one does not work below in the hope that someone can help me.
Function 1 JSP:
<form:form id="sform" action="DealMaintEdit.htm" method="POST" commandName="dmForm">
<table border="1">
<tr><td><spring:message code="label.DealID"/></td>
<td><form:input path="deal.dealId" readonly="true" size="6"/></td>
</tr>
<tr><td><spring:message code="label.Code"/></td>
<td><form:input path="deal.stkCode" maxlength="5" size="6"/>
<form:errors path="deal.stkCode" cssClass="error"/></td>
</tr> .....other fields skipped....
</table>
<form:hidden path="mode"/>
<form:hidden path="butt" value="Save"/>
<input form="sform" type="submit" value="<spring:message code="label.Save"/>"/>
</form:form>
Function 1 dmForm:
#Component
public class DealMaintForm {
private String mode;
private String butt;
#Valid
private Deal deal;
.....
Function 1 controller:
#RequestMapping(value = "/DealMaintEdit.htm")
public String dealMaintEdit(#ModelAttribute("dmForm") #Valid DealMaintForm form,
#RequestParam("butt") String butt, BindingResult result, Map model) {
I have if (result.hasErrors()) in the controller but it didn't execute before hitting the error.
Function 1 Deal:
public class Deal implements Serializable {
private Long dealId;
private DealType type;
#NotNull
#Min(1)
#Max(99999)
private int stkCode;
......
The funny thing is, if there is validation error, it hits the syntactically incorrect error before reaching the controller method. Say if I input 2 to stkCode it works fine, but if I change the #Min(1) in stkCode to #Min(5), input an 2 in stkCode will hit syntactically incorrect. Other fields are the same.
My other web form that works looks like this.
Function 2 JSP:
<form:form action="StockMaint2Edit.htm" method="POST" commandName="smForm">
<form:hidden path="mode"/>
<form:hidden path="stk.id"/>
<table border="1">
<tr>
<td><spring:message code="label.Code"/></td>
<td><form:input path="stk.code"/><form:errors path="stk.code" cssClass="error"/></td>
</tr>
<tr>
<td><spring:message code="label.Name"/></td>
<td><form:input path="stk.name" /><form:errors path="stk.name" cssClass="error"/></td>
</tr>
</table>
<form:hidden path="butt" value="Save"/>
<input type="submit" value="<spring:message code="label.Save"/>"/>
</form:form>
Function 2 smForm
#Component
public class StockMaintForm2 {
private String mode;
private String butt;
#Valid
private Stock stk;
.....
Function 2 Controller
#RequestMapping(value = "/StockMaint2Edit.htm")
public String stockMaintEdit(#ModelAttribute("smForm") #Valid StockMaintForm2 form,
BindingResult result, Map model) {
if (result.hasErrors()) {
System.out.println("edit has errors");
List<FieldError> errList = result.getFieldErrors();
for (FieldError fe : errList) {
......
Function 2 Stock
public class Stock implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
#NotNull
#Min(1)
#Max(99999)
private int code;
#NotBlank
private String name;
....
Function 2 works with all validation error msg displayed correctly. No syntactically incorrect error.
Compared them several times and still not able to spot the difference and the cause of the problem. Please let me know if I should provide more info.
I guess this causes the error,
<form:hidden path="butt" value="Save"/>
You can try with normal hidden field as ,
<input type="hidden" name="butt" value="Save"/>
in your Function 1 JSP .
As you have used the spring:form tag . butt will be not available explicitly in the request. you need to get it from the model attribute or you can make the input field simply with html tag. so that it will be available in the request.
Thank you for all the responses. I should reply individually, but after adding the first comment I am not able to add further comments. So I reply here. With your help I focused on the "butt". Since it is in both the form and the param, I delete the #RequestParam and obtain it from the form, and then it works.
#RequestMapping(value = "/DealMaintEdit.htm")
public String dealMaintEdit(#ModelAttribute("dmForm") #Valid DealMaintForm form,
BindingResult result, Map model) { ......
#SergeBallesta, tried your suggestion by moving the butt to the end and it also works. How can I add yours as useful comment?
#RequestMapping(value = "/DealMaintEdit.htm")
public String dealMaintEdit(#ModelAttribute("dmForm") #Valid DealMaintForm form,
BindingResult result, Map model, #RequestParam("butt") String butt) { ....
#SanKrish, tried the normal html "hidden" but it doesn't work. Furthermore, since the problem is missing param and my #RequestParam is redundant, I experimented it with this
#RequestMapping(value = "/DealMaintEdit.htm")
public String dealMaintEdit(#ModelAttribute("dmForm") #Valid DealMaintForm form,
#RequestParam("butt2") String butt2, BindingResult result, Map model) { ....
and in the jsp
<form:form id="sform" action="DealMaintEdit.htm?butt2=abcd" method="POST" commandName="dmForm">
it also doesn't work. Though the problem some how solved, there remains 2 questions:
Why "butt2" still has syntactically error?
If the original problem was due to missing param, why it only happens when there is validation error?
and I see that you're actually merging your annotations on the methods:
could you try to get a line break there:
public class Stock implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
#NotNull
#Min(1)
#Max(99999)
private int code;
#NotBlank
private String name;
....

#NumberFormat Annotation not working

Trying to show the currency symbol in JSP but I don't see it. Did my research and I just don`t know what more should I add to get it working. This is what I have.
<mvc:annotation-driven />
Controller
#NumberFormat(style = Style.CURRENCY)
private Double value = 50.00;
#ModelAttribute("value")
#NumberFormat(style = Style.CURRENCY)
public Double getValue() {
return value;
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView loadForm(#ModelAttribute("user") User user) {
ModelAndView instance
modelAndView.addObject("value", 100.00);
return modelAndView;
}
JSP
<spring:bind path="value">
<input type="text" name="${value}" value="${value}"/>
</spring:bind>
<spring:bind path="value">
${value}
</spring:bind>
Output
<input type="text" name="value" value="100.0"/>
100.0
Try using the string literal value for the name attribute instead of resolving it with EL
<spring:bind path="value">
<input type="text" name="value" value="${value}"/>
</spring:bind>
Also, move the field value into a new object. Currently I do not believe the code is using the field in the controller or the getter in the controller.
public class MyForm(){
#NumberFormat(style = Style.CURRENCY)
private Double value = 50.00;
#ModelAttribute("value")
#NumberFormat(style = Style.CURRENCY)
public Double getValue() {
return value;
}
}
Then add the object to the model in the controller:
#RequestMapping(method = RequestMethod.GET)
public ModelAndView loadForm(#ModelAttribute("user") User user) {
ModelAndView instance
modelAndView.addObject("myForm", new MyForm());
return modelAndView;
}
Then access via the jsp:
<spring:bind path="myForm.value">
<input type="text" name="${status.expression}" value="${status.value}"/>
</spring:bind>
<spring:bind path="myForm.value">
${status.value}
</spring:bind>
The major issue at the moment with the code is that it is not using the field/accessor, it is simply placing a value in the model, which does not use any of the annotated fields/methods.
References:
http://www.captaindebug.com/2011/08/using-spring-3-numberformat-annotation.html#.UOAO_3fghvA
How is the Spring MVC spring:bind tag working and what are the meanings of status.expression and status.value?

how to read the already existing bean in jstl|form?

I have simple form with SpringMVC, I want retrieve my already had bean by pbid. the problem is the server side I can get the already setting bean, but the jsp side it always get new bean. Can I use the #ModelAttribute("productbean") to received some parameters to get the bean store in my server-side? how to do it? The jstl|form seem always get new form
<form:form method="post" modelAttribute="productbean" action="">
<table>
</tr>
<tr>
<td width="118"><form:label for="name" path="name" > Production Name:</form:label></td>
<td colspan="2"><form:hidden path="pbid"/><form:input path="name" type="text" size="50" /></td>
</tr>
...
My controler is like:
#RequestMapping(value="/createproduct", method=RequestMethod.GET)
public String getProduct(HttpServletRequest req, Model model,
#RequestParam(value = "pbid", required = false, defaultValue = "") String spbid) throws MalformedURLException {
UUID pbid;
if(spbid.isEmpty())pbid=UUID.randomUUID();
else pbid=UUID.fromString(spbid);
ProductBean tmp;
if(!products.containsKey(pbid)){
tmp=createbean();
pbid=tmp.getPbid();
System.err.println("============new productbean===============\n");
}else{
tmp=products.get(pbid);
System.err.println(tmp.getMpf().size());
System.err.println(tmp.getMpf().printFileNameList());
}
.....
#ModelAttribute("productbean")
public ProductBean createbean(){
ProductBean productbean=new ProductBean(context.getRealPath(filepath));
products.put(productbean.assignID(), productbean);
return productbean;
}
Add session attribute annotation over class
#SessionAttributes("productbean")
#controller
public class test() {
}

Resources