Baeuerle Associates, LLC
 5570 Leeward Ln
 Tilghman, MD 21671

Controller

Page event (request) handling and controller design:

The default Grails CRUD application consist of one controller (responsible for requests) and four pages (views). These are list, create, edit, show pages. A new controller instance is created for each request. The request can handle in different ways:

  • Call a view (page)
  • Interaction with a domain, service, get data for passing it to a view or from a view (page)
  • Writing directly to the response writer
  • Redirecting to another action in the same controller or to another controller

If no action is specified by calling a controller (e.g. URI) the controller handles a default action.

  • If the controller defines only one action, it becomes the default action.
  • A action called index, becomes the default action.
    def index = { redirect(action:list) }
  • A value of a defined string property called defaultAction.
    def defaultAction = ‘list’

A CRUD controller has the actions: index (default action), list (show list of entries), create (render create view from list), save (save new domain from create view), edit (render edit view from list), update (update existing domain from edit view), delete (delete domain from list view), show (render show view from list).

Beyond CRUD applications the page flow (views) and event (request) handling becomes more complex and complicated. Even for CRUD applications, I am wondering why we need 3 views for create, edit and show.

Controller design considerations:

Assuming a number of views in a flow to collect data where at the end follows an action (for e.g. save).

  • One controller for one view.
  • One controller for all views with action, redirect etc.
  • One controller for all views with a web flow http://grails.org/WebFlow.

I suggest the third solution, even if I have good experience with solution 1. The advantage I see is the maintenance, that all requests are covered in one controller which belongs to this view flow. The grails web flow has 5 different scope you can utilize:

  • request - Stores an object for the scope of the current request
  • flash - Stores the object for the current and next request only
  • flow - Stores objects for the scope of the flow, removing them when the flow reaches an end state
  • conversation - Stores objects for the scope of the conversation including the root flow and nested subflows
  • session - Stores objects inside the users session

A sample for a web flow could look like this: We assume a document flow with a couple of views, the name of the controller is DocumentController.

Example:

class DocumentController implements ApplicationContextAware { 
        
     ApplicationContext applicationContext
     def index = { redirect(action:'documentWork') }
  
     def documentWorkFlow = {

           // show list view
           getDocumentsAction {
                   action {
                         flow.keyNo = session.keyNo
                         flow.doc = null // flow property
                         if(!params.max) params.max = 20
                         if(!params.offset) params.offset = 0
                         def docList = Doc.findAllByKeyNo(flow.keyNo,  [sort:'keyNo', order:'desc',
                                                                       max:params.max, offset:params.offset])
                         int listCount = Doc.countByKeyNo(flow.keyNo)
                  
                         return [ docList: docList, listCount:listCount]
                   }
                   on("success").to "list"
           }
     } // documentWorkFlow

     /* list view events */
     list {
           on("create").to  "viewAction"
           on("edit").to    "viewAction"
           on("show").to    "viewAction"
           on("delete").to  "deleteAction"
     }

     //initial viewAction for the view
     viewAction {
           action {
               log.debug("view params <${params}>")
                 String function = "create"
               if ( params._eventId_editView )
                   function = "edit"
                 else if ( params._eventId_copyView )
                   function = "copy"
                 //load flow.doc domain for the specific view (page)
                 flow.doc = initialPageSetup(params, function)
         }
         on ("success").to "viewDocument"
     }

     //process viewDocument (2 buttons save, back)
     viewDocument {
         on("save") {
                 //Validation
                 DocumentCommand docCmd ->
                 if(docCmd.hasErrors()) {
                       flow.doc.properties = params
                       flow.docCmd = docCmd
                       return error() // stays in the page
               }
                 else {
                         log.debug ('viewDocument.save')
                         flow.doc.properties = params
                         if ( !flow.doc.hasErrors() && flow.doc.save() )
                             flash.message = "Document saved."
                       else
                             flash.message = "Document not saved."
                       return error() // stays in the page
                 }
           }.to "getDocumentsAction"
         on("backl").to "getDocumentsAction"
           on(Exception).to "handleError"
     }

     //delete list entry (row)
     deleteAction {
         action {
                 def doc = Doc.get(params.id) // list enrtry
                 doc.delete()
                 flash.message = "Document deleted."
         }
         on ("success").to "getDocumentsAction"
     }

     //show handleError view
     handleError() {
         action{
                 log.error("Webflow Exception occurred: ${flash.stateException}", flash.stateException)
           }
           on("back").to "getDocumentsAction"
     }
}

 

[Home] [Company] [Services] [Grails] [Grails beyond CRUD] [Domain] [Controller] [View] [Validation] [Links]

For additional questions please eMail to Baeuerle Associates, LLC
©Copyright 2003 Baeuerle Associates, LLC. All rights reserved.