Tuesday, October 06, 2009

Griffon Tip: MVC Groups Revisited

My last post about MVC groups caused a bit of confusion, so I thought I'd follow up with a bit of clarification and, as a bonus, I'll also throw in a new trick for working with MVC groups.

MVC Groups for Dialogs
The withMVC method I introduced in the previous post was meant to help manage the lifecycle of short-lived MVC groups, such as those used in simple dialogs.  In cases like this, the MVC group only has to exist for as long as the dialog is visible.  withMVC was very much inspired by File#withInputStream which handles opening and closing of the input stream like withMVC handles creating and destroying of the MVC group.  Below is a complete example of showing an MVC group as a dialog:

NewProjectWizardModel.groovy
@Bindable class NewProjectWizardModel {
    String name
    String filePath
}

NewProjectWizardView.groovy
panel(id:'options', layout: new MigLayout('fill'), border: etchedBorder()) { 
    // project name
    label('Name:')
    textField(text: bind(source: model, sourceProperty:'name', mutual:true), constraints: 'growx, span, wrap')
    
    // directory
    label('Directory:')
    textField(text: bind(source: model, sourceProperty:'filePath', mutual:true), constraints:'width min(200px), growx')
    button(action: browseAction, constraints: 'wrap')
}

NewProjectWizardController.groovy
class NewProjectWizardController {
    def model
    def view

    void mvcGroupInit(Map args) {}

    def actions = [
        browse': { evt = null ->
            def file = Dialogs.showSaveDirectoryDialog("New Project", null, app.appFrames[0])
            if (file) { 
                model.filePath = file.absolutePath
                if (!model.name) { model.name = file.name }
            }
        }
    ]

    def show() {
        if (Dialogs.showCustomDialog("Create New Project", view.options, app.appFrames[0])) {
            // perform some logic
        }
    }
}

Dialogs.showCustomDialog() just uses JOptionPane to show a custom dialog with a component from the view.  I can show this dialog in response to a menu option, a button click, or whatever with the snippet from last post:
withMVC('NewProjectWizard', 'newProject', [:]) { mvc ->
    def project = mvc.controller.show()
    if (project) {
        // do something
    }
}

Hopefully that's a bit more clear and shows how MVC groups can be used as dialogs.  Now let's look at another use of MVC groups: embedding them as components in other views.

Embedding MVC Groups in Views
MVC groups work well as dialogs but they can also be embedded directly in the views of other MVC groups.  This is useful because it allows you to create re-usable components that you can embed in your views as needed.  Embedding is relatively straightforward:
widget(id:'sidePanel', buildMVCGroup('Project', 'project').view.root)

We build the MVC group and grab a component from the view (in this case it was called 'root' but it could be any id).  If we need to access something in the group, we can reference it by id:
def model = app.models['id']
def view = app.views['id']
def controller = app.controllers['id']

Hopefully these tips help you use MVC groups a bit more effectively and bring a bit more modularity to your Griffon app.

No comments: