views:

341

answers:

2

Please bare with me as I try to abstract this problem.

Say your writing a web page for fruit vendors using Spring MVC's SimpleFormController, version 2.5.6. On this page the vendor can do simple things like change their name or their address. They can also change their inventory based on a drop down list filled with present inventory selections.

When this drop down list selection changes, the entire form changes to match the inventory of what has been selected. So one stock selection may have bananas and pears, another may have melons, blueberries and grapefruit. Image to demonstrate what I am talking about.

Inside each inventory selection is a input field that needs to be propagated back to the database, for the sake of this example let's say that the user enters the number of fruit.

The way this is modeled in the database is that each Stock name is stored in a table, which has a one to many relationship with the contents of each stock, which would be the type of fruit in this example. Then the type of fruit has a one to many relationship with the quantity the vendor selects. Stock name and the type of fruit in each stock are stored in the database and are unchangeable by the user, with the connected fruit quantity table being editable.

My question is, how do you model the form described above in Spring MVC?

I've tried overriding the isFormChangeRequest and onFormChange to facilitate the form change, but I think I may be misunderstanding the intent of these methods. When I change my backing command object the next time the page is post it tries to bind the request into the form, which breaks if you adjust the size of the Stock array (say from 3 to 2, it will try and bind into the 3rd value, even if it is empty).

Can anyone help me out with this?

A: 

If you have a limited amount of different stocks, you can use different handler mappings for each one with a different backing model:

@RequestMapping(params="stock=example1") ModelAndView handleExample1(@ModelAttribute("stock") ApplesOrangesPears stockObject)

@RequestMapping(params="stock=example2") ModelAndView handleExample2(@ModelAttribute("stock") BananasPotatos stockObject)

But I guess that is not the case, there are a lot of different stock types and they are dynamic. In that case you can register custom property editor (@InitBinder), and determine dynamically the actual type of the backing object for the inventory, then validate, and convert to or from it explicitly.

sibidiba
I am not sure if I follow your answer. I'm not familiar with the Spring MVC 3.0 style annotations so I have trouble following the first part and for the second part, I am not sure that I understand what benefit a custom property editor would provide. Could you please elobratate? Thanks.
James McMahon
A: 

What I ended up doing is firing a JavaScript event when the selection in the drop down is changed. This JavaScript (seen below) generates a URL based on the selection of the drop down and uses a location.replace to go to the new URL, which causes the controller to generate a new form.

Using this method over overriding the isFormChangeRequest and onFormChange has allowed me to avoid binding errors caused by left over post data.

function changeUrl(selectionValue) {
    var param = getParams();
    param["dropdownselection"] = selectionValue;
    window.location.replace(getBaseUrl() + buildQueryString(param));
}

//taken from http://javascript.about.com/library/blqs1.htm
function getParams() {
    var qsParm = new Array();
    var query = window.location.search.substring(1);
    var parms = query.split('&');
    for (var i = 0; i < parms.length; i++) {
        var pos = parms[i].indexOf('=');
        if (pos > 0) {
            var key = parms[i].substring(0,pos);
            var val = parms[i].substring(pos+1);
            qsParm[key] = val;
        }
    }
    return qsParm;
}

function getBaseUrl() {
    var url = document.location.toString();
    if (url.indexOf('?') != -1) {
        url = url.substring(0, url.indexOf('?'));
    }
    return url;
}

function buildQueryString(param) {
    var queryString = "?";
    for (var key in param) {
        queryString += key + "=" + param[key] + "&";
    }
    //remove last "&"
    return queryString.substring(0,queryString.length - 1);
}
James McMahon