How can I write a Spring MVC controller method with a variable number of form fields and files?

I am developing a Spring MVC 3.1 application (using controller annotations) and I am facing a problem, I am not sure how to solve it. I have a page that, depending on the data, builds a form for the user to POST. The form can contain a variable number of fields and any number of files. I haven't been able to find an example of how to write a controller method to handle this.

Most of the examples I've seen use static forms, so a developer can write an object to bind to the form's data. In my case, however, since the length of the form varies, I have no such option (I think).

Is this possible with Spring?

+3


source to share


2 answers


Here's an example from an old project.

  • The form. Notice the modelAttribute in the form: form tag. This is what Spring tells it to bind a support object to a form and vice versa. I've removed a lot of fields for brevity. Only regions (ambiguous) and the price remain.

    <form:form method="post" modelAttribute="bindableCourse" id="courseForm">
    
    <%-- regions --%>
    <div class="default-block">
        <form:label path="regions">
            Regions
        </form:label>
        <c:forEach items="${allRegions}" var="region">
            <span class="inlineCheckBox"><form:checkbox path="regions" value="${region.id}" label="${region.name}"/></span>
        </c:forEach>
        <div><br><a href="#" id="selectAllRegions"><spring:message code="course.form.regions.selectall.linktext"/></a><br><br></div>
        <form:errors path="regions" cssClass="form-error"/>
    </div>
    
    <%-- price (formatted) --%>
    <div class="default-block">
        Price</form:label>
        <form:input path="formattedPrice" cssErrorClass="form-input-error" size="10"/>
        <spring:message code="course.form.currencysymbol"/> <spring:message code="course.form.price.perperson"/>
        <form:errors path="formattedPrice" cssClass="form-error"/>
    </div>
    
    
    </form:form>
    
          

  • The controller has a GET and POST method for this form. In the GET method, we add the bindable course to the model. Note that the name "bindableCourse" matches the modelAttribute in the JSP form. In the POST method, we capture that the user has submitted a BindableCourse for this.

    @Controller
    public class CourseController {
    
    
    // This is called when accessing the form for the first time
    @RequestMapping(value = "/admin/course/add", method = RequestMethod.GET)
    public String newCourse(Model model, Locale locale) {
    
        BindableCourse bindableCourse = new BindableCourse();
        model.addAttribute("bindableCourse", bindableCourse);
        model.addAttribute("mainContent", "forms/editCourse");
        addDataToModel(model, locale, companyService.getCurrentlyLoggedInCompany());
    
        return "adminMain";
    }
    
    // This is called when submitting the form. 
    // Note that Spring created a BindableCourse for us, 
    // filled with the user entered values. This is Spring binding in action.
    // Happens behind the scenes.
    @RequestMapping(value = "/admin/course/add", method = RequestMethod.POST)
    public String addCourse(@ModelAttribute("bindableCourse") BindableCourse bindableCourse, BindingResult result,
                            RedirectAttributes redirectAttributes, Model model, Locale locale) {
    
        validator.validate(bindableCourse, result);
    
        if (!result.hasErrors()) {
            Course course = courseService.save(bindableCourse);
            bindableCourse.setPublishable(true);
            redirectAttributes.addFlashAttribute("valid", "true");
            return "redirect:/admin/course/" + course.getId();
        }
    
        addDataToModel(model, locale, companyService.getCurrentlyLoggedInCompany());
        model.addAttribute("valid", "false");
        model.addAttribute("mainContent", "forms/editCourse");
        return "adminMain";
    }
    
          

    }

  • And finally, the "BindableCourse" form object. It's just a simple Java object that transfers data to and from a form.

    public class BindableCourse {
    
    private Long id;
    private String name;
    private String shortDescription;
    private String longDescription;
    private String certificateText;
    private String duration;
    // A multivalued property. The user can select multiple regions with multiple checkboxes
    // But you can also use multiple fields with the same name like address[0], address[1], etc
    private List<Long> regions = new ArrayList<Long>();
    private long category;
    private List<String> tags = new ArrayList<String>();
    private String formattedPrice;
    private boolean certificate;
    private String certificateName;
    private List<Long> times = new ArrayList<Long>();
    private List<String> dates = new ArrayList<String>();
    private List<Long> options = new ArrayList<Long>();
    private Calendar firstPublished;
    private boolean published;
    private boolean publishable;
    private String linkToSite;
    
    // getters and setters ommitted
    
    
    }
    
          



In the JSP form I am using, you can see that multi-valued regions are provided by the backend. I am repeating the collection "allRegions". They are added to the controller for the model in the addDataToModel () method (not shown here). In your scenario, you will add new fields to your form in the frontend using Javascript, but the idea is the same.

+1


source


Use a wrapper to encapsulate your form data.

eg:

public class WrapperForm {

    private List<String> Fields;

    public List<String> getFields() {
        return Fields;
    }

    public void setFields(List<String> fields) {
        Fields = fields;
    }
}

      

then you can put x fields in it (via controller method):



 @RequestMapping(method = RequestMethod.POST)
protected String processFinish(ModelMap model) {

    //process request -> need x fields in form


    WrapperForm formFields = new WrapperForm();
    formFields.setFields(new ArrayList<String>());
    for (int i = 0; i<x;i++){
        formFields.getFields().add("anyDefaultValue");
    }

    model.addAttribute("formFields", formFields);
    return "youJsp";
}

      

last step: your jsp.

you just need a basic JSTL library to put inside your blogs like:

<c:forEach items="${formFields.fields}" varStatus="i">
            <form:input path="fields[${i.index}]"/>
</c:forEach>

      

0


source







All Articles