views:

1085

answers:

3

I have the following situation. I have a validator to validate my command object and set the errors on a Errors object to be displayed in my form. The validator is invoked as expected and works okay, but the errors i set on the Errors objects are not displayed, when i am sent back to my form because of the validation errors.

Validator:

public void validate(Object obj, Errors err) {   
    MyCommand myCommand = (MyCommand) obj;   
    int index = 0;   
    for (Field field : myCommand.getFields()) {   
        if (field.isChecked()) {   
            if ((field.getValue() == null) || (field.getValue().equals(""))) {   
                err.rejectValue("fields[" + index + "].value", "errors.missing");   
            }   
        }   
        index++;   
    }   
    if (myCommand.getLimit() < 0) {   
        err.rejectValue("limit", "errors.invalid");   
    }   
}

Command:

public class MyCommand {   
    private List<Field> fields;   
    private int limit;   

    //getters and setters   
}   

public class Field {   
    private boolean checked;   
    private String name;   
    private String value;   

    //getters and setters   
}

Form:

    <form:form id="myForm" method="POST" action="${url}" commandName="myCommand">   
    <c:forEach items="${myCommand.fields}" var="field" varStatus="status">   
        <form:checkbox path="fields[${status.index}].checked" value="${field.checked}" />   
        <c:out value="${field.name}" />   
        <form:input path="fields[${status.index}].value" />   
        <form:errors path="fields[${status.index}].value" cssClass="error" /></td>   
        <form:hidden path="fields[${status.index}].name" />   
    </c:forEach>   
    <fmt:message key="label.limit" />    
    <form:input path="limit" />   
    <form:errors path="limit" cssClass="error" />   
</form:form>

Controller:

    @RequestMapping(value = REQ_MAPPING, method = RequestMethod.POST)   
    public String onSubmit(Model model, MyCommand myCommand, BindingResult result) {   
    // validate   
    myCommandValidator.validate(myCommand, result);   
    if (result.hasErrors()) {   
        model.addAttribute("myCommand", myCommand);   
        return VIEW;   
    }   

    // form is okay, do stuff and redirect   
}

Could it be that the paths i give in the validator and tag are not correct? The validator validates a command object containing a list of objects, so that's why i give a index on the list in the command object when registering an error message (for example: "fields["+index+"]".value). Or is it that the Errors object containing the errors is not available to my view?

Any help is welcome and appreciated, it might give me a hint or point me in right direction.

A: 

The line err.rejectValue("fields[" + index + "].value", "errors.missing"); will not do what you are trying to achieve. The first argument must be a property name of your Command bean, in your case fields.

To give the user a message you will have to use a parameterizable message in your properties file, eg. myform.field.error = you have an error in field {0}

So, use this method of the Errors object:

void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) 

Where you pass index into errorArgs

Hans Westerbeek
Thanks for your response mirror303. What I do not get precisely is what you mean with passing the index into errorArgs. I was under the assumption that errorArgs are arguments for the parameterized message in my properties file and have nothing to do with an indexed command property (like List). But probably I do not get what you're saying. So, do you mean that errorArgs should have the value "fields["+index+"].value"?
Art Vandelay
yes index, as in the method variable you use in the code you posted
Hans Westerbeek
But the index is an int and not an Object[] and fields[index].value is a String, so i do not see how using the index i can supply an Object[] as parameter.
Art Vandelay
You can create an array of java.lang.Integer objects (which will have only one element), and create an Integer object from the primitive int index that you have in your code.Now please vote up my answer :)
Hans Westerbeek
+1  A: 

@Yuri.Bulkin I tried adding but still nothing is being dislayed. The error for property 'limit' of class Field is also not being shown although it is a regular property.

@mirror303 I changed my validator code to "err.rejectValue("filters", "errors.missing", new Object[] {index}, "missing");" and JSP to ""

but still nothing is being displayed.

And I tried voting up your answer but I am not allowed to because I have be logged in, but I do not have an account, sorry :-)

Art Vandelay
A: 

I found what is your problem. property fields in object myCommand always null. You need to create constructor for class MyCommand in which you need to use LazyList from Apache Commons or AutoPopulatingList from Spring to create auto growing list of Field

In example (using AutoPopulatingList):

public class MyCommand {   
    private List<Field> fields;   
    private int limit;   

    //getters and setters
    //...
    //Constructor
    public MyCommand() {
        fields = new AutoPopulatingList<Field>(Field.class);
    } 
}
Yuri.Bulkin
I finally got my form validation to work. As it turned out, Spring was adding the BindingResult object to the model, but under a different name (the classname beginning with lower case) than the command name I am using in my form tag. Changing the command name to reflect the name Spring is using to add the BindingResult to the model, got my form validation to work as expected. Anyway, thanks for you effort and help. ps. Why the need for LazyList? Because without it my form seems to work okay.
Art Vandelay