FieldError vs ObjectError vs Global Error - spring

I'm reading through the Spring 4 API and I'm trying to understand the difference between FieldError, ObjectError and global error, all in the context of a BindingResult. I'm guessing that global errors are another name for ObjectErrors since getGlobalError() returns an ObjectError.
The context is that I am looking to simply print error messages after some form validation and want to know how I can avoid an instanceof check, as in this accepted answer. Can I just use FieldError and ignore object errors? What would I be missing if I only logged FieldErrors?
I've tried a few scenarios but don't yet see the distinction. Will look through some source in the meantime.

I'm guessing that global errors are another name for ObjectErrors
since getGlobalError() returns an ObjectError.
Actually a "global error" is any ObjectError that is not an instance of a FieldError see source of getGlobalErrors()
What would I be missing if I only logged FieldErrors?
Any ObjectErrors that code registered as a "global error" e.g. by calling BindingResult.reject(errorCode, errorArgs, defaultMessage).
See also the javadoc for rejectValue(field, errorCode, errorArgs, defaultMessage). Typically errors are registered against fields of the validated/bound object (e.g. the model value whose attribute matches the modelAttribute tag of the Spring form tag) as opposed to the object itself.
Following are a couple of ways to create global errors:
Assuming it's the root form object and not a nested object that's being validated via a Spring Validator implementation, you could add a "global error" (in the context of the specified bound root object) by passing null as the field name parameter of rejectValue. If the object being validated is a nested object, however, a FieldError would be registered against the nested object field. So it matters what is the nestedPath ("nested object graph") property of the target Errors object with respect to whether a general ObjectError or specific FieldError is added.
Via a JSR 303 constraint annotation applied at the class level. See example in which a model object is checked for pre-existence in a datastore.
Here's an example to reference the global vs the field level errors:
public class OverflowErrorsTag extends HtmlEscapingAwareTag {
public static final String OVERFLOW_ERRORS_VARIABLE_NAME = "overflowErrors";
public static final String GLOBAL_ERRORS_VARIABLE_NAME = "globalErrors";
private String name;
/**
* Set the name of the bean that this tag should check.
*/
public void setName(String name) {
this.name = name;
}
/**
* Return the name of the bean that this tag checks.
*/
public String getName() {
return this.name;
}
#Override
protected final int doStartTagInternal() throws ServletException, JspException {
Errors errors = getRequestContext().getErrors(this.name, isHtmlEscape());
Set<FieldError> subsequentErrors = Sets.newTreeSet((fe1, fe2) -> fe1.getField().compareTo(fe2.getField()));
Set<ObjectError> globalErrors = new HashSet<>();
if (errors != null) {
Set<String> firstErrorFields = new HashSet<>();
for (FieldError fieldError : errors.getFieldErrors()) {
if (firstErrorFields.contains(fieldError.getField())) {
subsequentErrors.add(fieldError);
} else {
firstErrorFields.add(fieldError.getField());
}
}
for (ObjectError objectError : errors.getGlobalErrors()) {
globalErrors.add(objectError);
}
}
if (subsequentErrors.isEmpty() && globalErrors.isEmpty()) {
return SKIP_BODY;
} else {
this.pageContext.setAttribute(OVERFLOW_ERRORS_VARIABLE_NAME, subsequentErrors, PageContext.REQUEST_SCOPE);
this.pageContext.setAttribute(GLOBAL_ERRORS_VARIABLE_NAME, globalErrors, PageContext.REQUEST_SCOPE);
return EVAL_BODY_INCLUDE;
}
}
#Override
public int doEndTag() {
this.pageContext.removeAttribute(OVERFLOW_ERRORS_VARIABLE_NAME, PageContext.REQUEST_SCOPE);
this.pageContext.removeAttribute(GLOBAL_ERRORS_VARIABLE_NAME, PageContext.REQUEST_SCOPE);
return EVAL_PAGE;
}
}
Then to display this tag containing both global and field errors in the view:
<spring-ext:overflowErrors name="newModelObject">
<div class="row">
<div class="large-12 columns">
<div class="alert panel">
<c:if test="${overflowErrors.size()>0}">
<p>There are multiple errors with your entry.</p>
<c:forEach var="error" items="${overflowErrors}">
${fn:toUpperCase(fn:substring(error.field, 0, 1))}${fn:toLowerCase(
fn:substring(error.field, 1,fn:length(error.field)))}:
<b><spring:message message="${error}" /></b>
<br/>
</c:forEach>
</c:if>
<c:forEach var="error" items="${globalErrors}">
<b><spring:message message="${error}" /></b>
<br/>
</c:forEach>
</div>
</div>
</div>
</spring-ext:overflowErrors>

Related

Getting a NumberFormatException with thymeleaf on th:field but not on others th:*

I'm rather new to Thymeleaf, so this maybe a newbie mistake. And I've been looking everywhere online for an answer and haven't found one. So sorry if this is really basic.
Basically I'm using Thymeleaf with SpringBoot 2.6.7 and I want to populate a object using a form. Something I've been able to do in the past, but this time for the first time the object I want to populate contains a list of other objects. And it's getting quite tricky.
My html looks like this :
<form action="#" th:action="#{/character}" th:object="${input}" method="post">
<div th:each="i : ${#numbers.sequence(0, input.attributes.size - 1)}">
<div th:object="${input.attributes[i]}">
<input type="range" min="1" th:max="*{maxValue}" th:field="*{value}"> <!-- this doesn't work -->
<p th:text="*{name} + ' = ' + *{value}"></p> <!-- this works -->
</div>
</div>
</form>
The error I get is java.lang.NumberFormatException: For input string: "i". So I'm guessing there's an issue with the parsing of attributes[i] when processing th:field.
I've tried to change the loop to <div th:each="attribute : ${input.attributes}"> (and the associated th:object) but that just made it worse, got the Neither BindingResult nor plain target object for bean name 'attribute' available as request attribute error message.
If it's any help, here is my controller :
#GetMapping("character")
public String startingForm(Model model) {
model.addAttribute("input", new FormInput());
return "character";
}
#PostMapping("character")
public String processingForm(#ModelAttribute FormInput input, BindingResult bindingResult, Model model)
throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException {
if(bindingResult.hasErrors()){
log.error("something went wrong");
}
// Other stuff
return "character";
}
My input class
#Getter
public class FormInput{
protected List<Attribute> attributes;
public FormInput(){
attributes= new ArrayList<>();
for (AttributeEnum ae : AttributeEnum.values()) {
attributes.add(new Attribute(ae.getName(), ae));
}
}
}
And the Attribute class
#Getter
public class Attribute {
protected String name;
protected AttributeEnum typeAttribute;
protected Integer value = 1;
protected Integer maxValue = 5;
public Attribute(String n, AttributeEnum ta){
name = n;
typeAttribute = ta;
}
public boolean setValue(Integer v){
if (v > maxValue ) return false;
value = v;
return true;
}
}
Does someone know a fix to this issue?
Just found the solution. So I'm posting the answer is anyone has the same issue.
<div th:each="i : ${#numbers.sequence(0, input.attributes.size - 1)}">
<input type="range" min="0" th:max="${input.attributes[i].maxValue}" th:field="${input.attributes[__${i}__].value}"> <!-- this works -->
</div>
Just had to replace attributes[i] with attributes[__${i}__].

Unable to submit list of LocalTime in Spring

I have this entity that contains a list of objects (omitted getters and setters and all irrelevant code for this example)
public class SampleType {
#OneToMany
List<SampleTypeTime> sampleTypeTimes = new ArrayList<SampleTypeTime>();
}
public class SampleTypeTime {
#DateTimeFormat(iso = ISO.TIME)
LocalTime time;
}
And i have this form that allows the user to select multiple hours..
<form th:object="${sampleType}" th:method="POST" th:action="#{#}">
<select th:field="*{sampleTypeTimes}" type="time" class="form-control" multiple>
<option th:value="00:00" th:text="${"00:00"}"></option>
<option th:value="01:00" th:text="${"01:00"}"></option>
... and so on
</select>
</form>
My controller:
#PostMapping("sampletype/")
public String productsTypesPost(#ModelAttribute SampleType sampleType, Model model) {
sampleTypeRepository.save(sampleType);
return "sampletype";
}
When i submit the form i get the following error message:
Field error in object 'sampleType' on field 'sampleTypeTimes': rejected value [00:00,02:00];
codes [typeMismatch.sampleType.sampleTypeTimes,typeMismatch.sampleTypeTimes,typeMismatch.java.util.List,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [sampleType.sampleTypeTimes,sampleTypeTimes];
arguments []; default message [sampleTypeTimes]]; default message [Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.List' for property 'sampleTypeTimes';
nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.project.SampleTypeTime' for property 'sampleTypeTimes[0]': no matching editors or conversion strategy found]
It seems to me that it struggles converting String[] to List, how can i get around this?
Edit: added controller class
As I said in my comment, form returns value of String while the array contains instances of SampleTypeTime. You need to tell Spring how to convert String to SampleTypeTime. For doing that you have to create implementation of PropertyEditor:
public class SampleTypeTimeEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
LocalTime time = LocalTime.parse(text);
SampleTypeTime sampleTime = new SampleTypeTime();
sampleTime.setTime(time);
setValue(appointment);
}
}
In this example code snippet I don't check the text if it has a right format or not. But in real code you should do it of course. After that add the created property editor to the DataBinder of your controller:
#Controller
public class FormController {
#InitBinder
public void initBinder(DataBinder binder)
binder.registerCustomEditor(SampleTypeTime.class, new SampleTypeTimeEditor());
}
...
}
Now Spring automatically will convert String to SampleTypeTime. You can get more information about PropertyEditor from this chapter of the official documentation. And here you can get details about DataBinder.

Spring Boot binding and validation error handling in REST controller

When I have the following model with JSR-303 (validation framework) annotations:
public enum Gender {
MALE, FEMALE
}
public class Profile {
private Gender gender;
#NotNull
private String name;
...
}
and the following JSON data:
{ "gender":"INVALID_INPUT" }
In my REST controller, I want to handle both the binding errors (invalid enum value for gender property) and validation errors (name property cannot be null).
The following controller method does NOT work:
#RequestMapping(method = RequestMethod.POST)
public Profile insert(#Validated #RequestBody Profile profile, BindingResult result) {
...
}
This gives com.fasterxml.jackson.databind.exc.InvalidFormatException serialization error before binding or validation takes place.
After some fiddling, I came up with this custom code which does what I want:
#RequestMapping(method = RequestMethod.POST)
public Profile insert(#RequestBody Map values) throws BindException {
Profile profile = new Profile();
DataBinder binder = new DataBinder(profile);
binder.bind(new MutablePropertyValues(values));
// validator is instance of LocalValidatorFactoryBean class
binder.setValidator(validator);
binder.validate();
// throws BindException if there are binding/validation
// errors, exception is handled using #ControllerAdvice.
binder.close();
// No binding/validation errors, profile is populated
// with request values.
...
}
Basically what this code does, is serialize to a generic map instead of model and then use custom code to bind to model and check for errors.
I have the following questions:
Is custom code the way to go here or is there a more standard way of doing this in Spring Boot?
How does the #Validated annotation work? How can I make my own custom annotation that works like #Validated to encapsulate my custom binding code?
This is the code what i have used in one of my project for validating REST api in spring boot,this is not same as you demanded,but is identical.. check if this helps
#RequestMapping(value = "/person/{id}",method = RequestMethod.PUT)
#ResponseBody
public Object updatePerson(#PathVariable Long id,#Valid Person p,BindingResult bindingResult){
if (bindingResult.hasErrors()) {
List<FieldError> errors = bindingResult.getFieldErrors();
List<String> message = new ArrayList<>();
error.setCode(-2);
for (FieldError e : errors){
message.add("#" + e.getField().toUpperCase() + ":" + e.getDefaultMessage());
}
error.setMessage("Update Failed");
error.setCause(message.toString());
return error;
}
else
{
Person person = personRepository.findOne(id);
person = p;
personRepository.save(person);
success.setMessage("Updated Successfully");
success.setCode(2);
return success;
}
Success.java
public class Success {
int code;
String message;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Error.java
public class Error {
int code;
String message;
String cause;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getCause() {
return cause;
}
public void setCause(String cause) {
this.cause = cause;
}
}
You can also have a look here : Spring REST Validation
Usually when Spring MVC fails to read the http messages (e.g. request body), it will throw an instance of HttpMessageNotReadableException exception. So, if spring could not bind to your model, it should throw that exception. Also, if you do NOT define a BindingResult after each to-be-validated model in your method parameters, in case of a validation error, spring will throw a MethodArgumentNotValidException exception. With all this, you can create ControllerAdvice that catches these two exceptions and handles them in your desirable way.
#ControllerAdvice(annotations = {RestController.class})
public class UncaughtExceptionsControllerAdvice {
#ExceptionHandler({MethodArgumentNotValidException.class, HttpMessageNotReadableException.class})
public ResponseEntity handleBindingErrors(Exception ex) {
// do whatever you want with the exceptions
}
}
You can't get BindException with #RequestBody. Not in the controller with an Errors method parameter as documented here:
Errors, BindingResult For access to errors from validation and data
binding for a command object (that is, a #ModelAttribute argument) or
errors from the validation of a #RequestBody or #RequestPart
arguments. You must declare an Errors, or BindingResult argument
immediately after the validated method argument.
It states that for #ModelAttribute you get binding AND validation errors and for your #RequestBody you get validation errors only.
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods
And it was discussed here:
https://github.com/spring-projects/spring-framework/issues/11406?jql=text%2520~%2520%2522RequestBody%2520binding%2522
For me it still does not make sense from a user point of view. It is often very important to get the BindExceptions to show the user a proper error message. The argument is, you should do client side validation anyway. But this is not true if a developer is using the API directly.
And imagine your client side validation is based on an API request. You want to check if a given date is valid based on a saved calendar. You send the date and time to the backend and it just fails.
You can modify the exception you get with an ExceptionHAndler reacting on HttpMessageNotReadableException, but with this exception I do not have proper access to which field was throwing the error as with a BindException. I need to parse the exception message to get access to it.
So I do not see any solution, which is kind of bad because with #ModelAttribute it is so easy to get binding AND validation errors.
I've given up on this; it is just not possible to get the binding errors using #RequestBody without a lot of custom code. This is different from controllers binding to plain JavaBeans arguments because #RequestBody uses Jackson to bind instead of the Spring databinder.
See https://jira.spring.io/browse/SPR-6740?jql=text%20~%20%22RequestBody%20binding%22
One of the main blocker for solving this problem is the default eagerly-failing nature of the jackson data binder; one would have to somehow convince it to continue parsing instead of just stumble at first error. One would also have to collect these parsing errors in order to ultimately convert them to BindingResult entries. Basically one would have to catch, suppress and collect parsing exceptions, convert them to BindingResult entries then add these entries to the right #Controller method BindingResult argument.
The catch & suppress part could be done by:
custom jackson deserializers which would simply delegate to the default related ones but would also catch, suppress and collect their parsing exceptions
using AOP (aspectj version) one could simply intercept the default deserializers parsing exceptions, suppress and collect them
using other means, e.g. appropriate BeanDeserializerModifier, one could also catch, suppress and collect the parsing exceptions; this might be the easiest approach but requires some knowledge about this jackson specific customization support
The collecting part could use a ThreadLocal variable to store all necessary exceptions related details. The conversion to BindingResult entries and the addition to the right BindingResult argument could be pretty easily accomplished by an AOP interceptor on #Controller methods (any type of AOP, Spring variant including).
What's the gain
By this approach one gets the data binding errors (in addition to the validation ones) into the BindingResult argument the same way as would expect for getting them when using an e.g. #ModelAttribute. It will also work with multiple levels of embedded objects - the solution presented in the question won't play nice with that.
Solution Details (custom jackson deserializers approach)
I created a small project proving the solution (run the test class) while here I'll just highlight the main parts:
/**
* The logic for copying the gathered binding errors
* into the #Controller method BindingResult argument.
*
* This is the most "complicated" part of the project.
*/
#Aspect
#Component
public class BindingErrorsHandler {
#Before("#within(org.springframework.web.bind.annotation.RestController)")
public void logBefore(JoinPoint joinPoint) {
// copy the binding errors gathered by the custom
// jackson deserializers or by other means
Arrays.stream(joinPoint.getArgs())
.filter(o -> o instanceof BindingResult)
.map(o -> (BindingResult) o)
.forEach(errors -> {
JsonParsingFeedBack.ERRORS.get().forEach((k, v) -> {
errors.addError(new FieldError(errors.getObjectName(), k, v, true, null, null, null));
});
});
// errors copied, clean the ThreadLocal
JsonParsingFeedBack.ERRORS.remove();
}
}
/**
* The deserialization logic is in fact the one provided by jackson,
* I only added the logic for gathering the binding errors.
*/
public class CustomIntegerDeserializer extends StdDeserializer<Integer> {
/**
* Jackson based deserialization logic.
*/
#Override
public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return wrapperInstance.deserialize(p, ctxt);
} catch (InvalidFormatException ex) {
gatherBindingErrors(p, ctxt);
}
return null;
}
// ... gatherBindingErrors(p, ctxt), mandatory constructors ...
}
/**
* A simple classic #Controller used for testing the solution.
*/
#RestController
#RequestMapping("/errormixtest")
#Slf4j
public class MixBindingAndValidationErrorsController {
#PostMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Level1 post(#Valid #RequestBody Level1 level1, BindingResult errors) {
// at the end I show some BindingResult logging for a #RequestBody e.g.:
// {"nr11":"x","nr12":1,"level2":{"nr21":"xx","nr22":1,"level3":{"nr31":"xxx","nr32":1}}}
// ... your whatever logic here ...
With these you'll get in BindingResult something like this:
Field error in object 'level1' on field 'nr12': rejected value [1]; codes [Min.level1.nr12,Min.nr12,Min.java.lang.Integer,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [level1.nr12,nr12]; arguments []; default message [nr12],5]; default message [must be greater than or equal to 5]
Field error in object 'level1' on field 'nr11': rejected value [x]; codes []; arguments []; default message [null]
Field error in object 'level1' on field 'level2.level3.nr31': rejected value [xxx]; codes []; arguments []; default message [null]
Field error in object 'level1' on field 'level2.nr22': rejected value [1]; codes [Min.level1.level2.nr22,Min.level2.nr22,Min.nr22,Min.java.lang.Integer,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [level1.level2.nr22,level2.nr22]; arguments []; default message [level2.nr22],5]; default message [must be greater than or equal to 5]
where the 1th line is determined by a validation error (setting 1 as the value for a #Min(5) private Integer nr12;) while the 2nd is determined by a binding one (setting "x" as value for a #JsonDeserialize(using = CustomIntegerDeserializer.class) private Integer nr11;). 3rd line tests binding errors with embedded objects: level1 contains a level2 which contains a level3 object property.
Note how other approaches could simply replace the usage of custom jackson deserializers while keeping the rest of the solution (AOP, JsonParsingFeedBack).
enter code here
public class User {
#NotNull
#Size(min=3,max=50,message="min 2 and max 20 characters are alllowed !!")
private String name;
#Email
private String email;
#Pattern(regexp="[7-9][0-9]{9}",message="invalid mobile number")
#Size(max=10,message="digits should be 10")
private String phone;
#Override
public String toString() {
return "User [name=" + name + ", email=" + email + ", phone=" + phone + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Controller.java
#Controller
public class User_Controller {
#RequestMapping("/")
public String showForm(User u,Model m)
{
m.addAttribute("user",new User());
m.addAttribute("title","Validation Form");
return "register";
}
#PostMapping("/")
public String register(#Valid User user,BindingResult bindingResult ,Model m)
{
if(bindingResult.hasErrors())
{
return "register";
}
else {
m.addAttribute("message", "Registration successfully... ");
return "register";
}
}
}
register.html
<div class="container">
<div class="alert alert-success" role="alert" th:text="${message}">
</div>
<h1 class="text-center">Validation Form </h1>
<form action="/" th:action="#{/}" th:object="${user}" method="post">
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Name</label>
<input type="text" class="form-control" id="exampleInputEmail1" aria-
describedby="emailHelp" th:field="*{name}">
<br>
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="alert alert-
danger"></p>
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Email</label>
<input type="email" class="form-control" id="exampleInputPassword1" th:field="*
{email}">
<br>
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="alert alert-
danger"></p>
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Phone</label>
<input type="text" class="form-control" id="exampleInputPassword1" th:field="*
{phone}">
<p th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}" class="alert alert-
danger"></p>
<br>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
According to this post https://blog.codecentric.de/en/2017/11/dynamic-validation-spring-boot-validation/ - you can add an extra parameter "Errors" to your controller method - eg.
#RequestMapping(method = RequestMethod.POST)
public Profile insert(#Validated #RequestBody Profile profile, Errors errors) {
...
}
to then get validation errors, if any, in that.
I think I should answer your questions in reverse order.
For your second question,
The #Validate annotation throws MethodArgumentNotValidException if there is an error during field validation. The object of this annotation contains two methods, getBindingResult(),getAllErrors() which gives details of validation error. You may create your custom annotation with AspectJ (AOP). But that's not needed here. Because your situation can be solved using the ExceptionHandler of SpringBoot.
Now your first question,
Please go through section 5 of this link Link. Actually it covers whole bean validation in spring boot. Your problem can be solved by section 5. Basic knowledge on general exception handling in spring boot may be good to understand it better. For that, I can share the query link on google for this topic ExceptionHandling.Please go through the first few results of it.

How to bind input elements to an arraylist element in Spring MVC?

How to bind input elements to an arraylist element in Spring MVC?
The view model:
public class AssigneesViewModel {
private int evaluatorId;
private int evaluatedId;
private String evaluatorName;
private String evalueatedName;
//getters and setters
}
The model attribute:
public class AssignEvaluationForm{
private ArrayList<AssigneesViewModel> options;
public ArrayList<AssigneesViewModel> getOptions() {
return options;
}
public void setOptions(ArrayList<AssigneesViewModel> options) {
this.options = options;
}
}
Controller
#RequestMapping(value="addAssignment", method = RequestMethod.GET)
public String addAssignment(Model model){
model.addAttribute("addAssignment", new AssignEvaluationForm());
return "addAssignment";
}
Then in the jsp i have 4 hidden inputs which represent the fields for the evaluatedId, evaluatorId, evaluatorName, evaluatedName -> options[0].
How i am going to write the jsp code to map those inputs with an element of the arrayList?
Update:
<form:form commandName="addAssignment" modelAttribute="addAssignment" id="addAssignment" method="POST">
//..........
<c:forEach items="${addAssignment.options}" var="option" varStatus="vs">
<div id="assigneesOptions" >
<form:input path="addAssignment.options[${vs.index}].evaluatedId" value="1"></form:input>
</div>
</c:forEach>
//..............
</form:form>
With this update i get the following error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'options[]' available as request attribute
<form:input path="addAssignment.options[${vs.index}].evaluatedId" value="1"></form:input>
Instead of this addAssignment.options[${vs.index}].evaluatedId
Use this -> option.evaluatedId
Or you might reach value with arraylist get -> ${addAssignment.options.get(vs.index).evaluatedId} , try to turn out that ${} jstl's call curly brackets. BTW i'm not sure this last example work on path="" attribute.

How to view data in Jsp when added in a model using spring mvc

I have the following flows for an application when a submit button is clicked:
1)The viewActivity method is called from ActivityController.java
ActivityController.java
#ActionMapping(params = "ActivityController=showActivity")
public void viewActivity(#RequestParam Integer index, ActionResponse response, Model model, #ModelAttribute Header header,
....
model.addAttribute("recoveryForm", new RecoveryForm(detailsResult.getDetails()));
response.setRenderParameter("ServiceController", "showService");
}
2) Then showRecovery method is called from serviceConroller as show below:
ServiceController.JAVA
#RenderMapping(params = "ServiceController=showService")
public String showRecovery(#ModelAttribute recoveryForm form, #ModelAttribute header header) {
.....
return service;
}
Then my service.jsp is displayed
Basically i have to display the value of a variable which is detailName found in DetailsResult.getDetails() object which i have added to my model as
it can be seen in viewActivity method found in ActivityController.java showed ealier.
I know when we add model.addAttribute it should be able to be displayed on this jsp using the following tag :
<form:input path="..." />
But in this case it is added to as a constructor argument as shown below:
model.addAttribute("recoveryForm", new RecoveryForm(detailsResult.getDetails()));
I have the following variable on my RecoveryForm:
public class RecoveryForm implements Serializable {
private CDetails Cdlaim;
private Action addAction;
private String addRemark;
private String remarks;
public RecoveryForm(CDetails Cdlaim) {
...
}
...
}
However i don't have the detailsResult in my RecoveryForm.
Any idea how i can get a value which is in DetailsResult.getDetails() in my service.jsp?
I believe you are looking at this the wrong way. The value of DetailsResult.getDetails() is obviously stored in RecoveryForm as a property somehow. So, I'm going to assume your RecoveryForm looks something like:
public class RecoveryForm {
private String details;
public RecoveryForm(String details) {
this.details = details;
}
public String getDetails() {
return details;
}
}
When you bind to a form in your jsp, you need to nest your <form:input ...> tag in a <form:form ...> tag:
<form:form commandName="recoveryForm" ...>
<form:input path="details" ... />
</form:form>
The commandName is key to telling the form the model object from which you will be pulling form values. In this case you are getting the details property from the RecoveryForm instance named recoveryForm. Make sense?

Resources