Search code examples
grailsgrails-controller

What is the "right" way to handle a mismatch between model, controller, and view in Grails?


Of course it would happen that my first Grails project presents a crazy mismatch between my data model and the presentation the UI designers want.

Here is a simplified statement of the problem space:

There are three ingredients in the grocery store: carrots, celery, and tomatoes. The purpose of the app is to save the user's shopping list: how many of each to buy.

The web designers want to accomplish this with multiple HTML SELECTs, so you select from the drop-down which ingredient you want to buy, then type the quantity next to it. If you want more ingredients, you click a JavaScript link that adds more SELECTs.

The design specifies that each drop-down is the same and that the number of lines are unlimited. So you can get a form back that has 5 lines, corresponding to 3 carrots, 9 celery, 2 celery, 5 carrots, and 1 tomato.

What I "should" produce out of this is an order for 8 carrots, 11 celery, and one tomato.

Not getting into all of the arguments about why this is bad UI design, I want to understand, practically, how to map the model/controller to the view given this kind of mismatch, both to submit and ultimately edit a stored submission.

My initial idea is:

  • For inbound data, construct a fresh map in the Controller out of the parameters and some logic to add up the values correctly into three new key/value pairs, passing that new map into the bindData method instead of the request params map itself.
  • For presenting the view for edit, use an afterInterceptor to rewrite that part of the model into the correct number of these SELECTs, recognizing that my original 5-line order will become three lines when it's presented for editing.

However, as I read about Command objects, I wonder if that would be a better approach.

I've read through many pages online but don't see any solutions for this kind of MVC mismatch.

Setting aside the obvious answer (fight the designers), what is the "Grails" way to handle this?


Solution

  • I would think your initial idea should work. A command object would be nice to move the logic of building the domain objects out of the controller methods, but not NEEDED. One could result in easier to read code, and some easier validation if you ever add more info to your shoppingList (name, date, etc). But something without a cmd object should work and I don't think would be wrong...

    GSP:

    <g:form .......>
    <div><g:select name="ingredient" from="${['Carrots','Celery','Tomato']}"><g:textField name="amount"/></div>
    <div><g:select name="ingredient" from="${['Carrots','Celery','Tomato']}"><g:textField name="amount"/></div>
    <div><g:select name="ingredient" from="${['Carrots','Celery','Tomato']}"><g:textField name="amount"/></div>
    <div><g:select name="ingredient" from="${['Carrots','Celery','Tomato']}"><g:textField name="amount"/></div>
    </g:form>
    

    Controller:

    def saveCart = {
      def shoppingList = [:]
      def ingredients = params.list('ingredient')
      def amounts = params.list('amount')
    
      ingredients.eachWithIndex() { obj, i ->
         if (shoppingList.containsKey(obj)) {
           shoppingList[obj] = shoppingList[obj] + amounts[i]
         } else {
           shoppingList[obj] = amt[i]
         }
      }
    
      // shoppingListshould have everything you need now
      ....
      ....
    }