(Quick Reference)

5.2 The MVC Pattern - Reference Documentation

Authors: Andres Almiray

Version: 1.2.0

5.2 The MVC Pattern

All Griffon applications operate with a basic unit called the MVC group. An MVC group is comprised of 3 member parts: Models, Views and Controllers. However it is possible to add (or even remove) members from an MVC group by carefully choosing a suitable configuration.

MVC groups configuration is setup in Application.groovy located inside griffon-app/conf. This file holds an entry for every MVC group that the application has (not counting those provided by plugins/addons).

Here's how a typical MVC group configuration looks like

mvcGroups {
    // MVC Group for "sample"
    'sample' {
        model      = 'sample.SampleModel'
        view       = 'sample.SampleView'
        controller = 'sample.SampleController'
    }
}

The order of the members is very important, it determines the order in which each member will be initialized. In the previous example both model and view will be initialized before the controller. Do not mistake initialization for instantiation, as initialization relies on calling mvcGroupInit on the group member.

MVC group configurations accept a special key that defines additional configuration for that group, as it can be seen in the following snippet

mvcGroups {
    // MVC Group for "sample"
    'sample' {
        model      = 'sample.SampleModel'
        view       = 'sample.SampleView'
        controller = 'sample.SampleController'
    }

// MVC Group for "foo" 'foo' { model = 'sample.FooModel' view = 'sample.FooView' controller = 'sample.FooController' config { key = 'bar' } } }

Values placed under this key become available to MVC members during the call to mvcGroupInit, as part of the arguments sent to that method. Here's how the FooController can reach the key defined in the configuration

class FooController {
    void mvcGroupInit(Map args) {
        println args.configuration.config.key
    }
}

While being able to set additional values under this key is a certainly an advantage it would probably be better if those values could be mutated or tweaked, probably treating them as variables, effectively making a group configuration work as a template. For that we'll have to discuss the mvcGroupManager first.

5.2.1 MVCGroupManager

This class is responsible for holding the configuration of all MVC groups no matter how they were defined, which can be either in Application.groovy or in an addon descriptor.

During the startup sequence an instance of MVCGroupManager will be created and initialized. Later the application will instruct this instance to create all startup groups as required. MVCGroupManager has a handful set of methods that deal with MVC group configuration alone; however those that deal with group instantiation come in 3 versions, with 2 flavors each (one Groovy, the other Java friendly).

Locating a group configuration is easily done by specifying the name you're interested in finding

def configuration = app.mvcGroupManager.findConfiguration('foo')

Once you have a configuration reference you can instantiate a group with it by calling any of the variants of the create method

def configuration = app.mvcGroupManager.findConfiguration('foo')
def group1 = configuration.create('foo1')
def group2 = configuration.create('foo2', [someKey: 'someValue'])
// the following will make the group's id match its name
def group3 = configuration.create()
def group4 = configuration.create(someKey: 'someValue')

Be careful that creating groups with the same name is usually not a good idea. The default MVCGroupManager will complain when this happens and will automatically spit out an exception. This behavior may be changed by setting a configuration key in Config.groovy

griffon.mvcid.collision = 'warning' // accepted values are 'warning', 'exception' (default)

The manager will log a warning and destroy the previously existing group before instantiating the new one when 'warning' is the preferred strategy

Now, even though you can create group instances based on their configurations the preferred way is to call any of createMVCGroup, buildMVCGroup or withMVCGroup methods. These methods are available to the app property every GriffonArtifact has, which points to the currently running application. Griffon artifacts also inherit these methods as part of their default contract. Finally, any class annotated with the MVCAware AST transformation will also gain access to these methods.

Groups will be available by id regardless of how they were instantiated. You can ask the mvcGroupManager for a particular group at any time, for example

def g1 = app.mvcGroupManager.groups.foo1
def g2 = app.mvcGroupManager.findGroup('foo1')
def g3 = app.mvcGroupManager.foo1
assert g1 == g2
assert g1 == g3

It's also possible to query all models, views, controllers and builders on their own. Say you'd want to inspect all currently instantiated models, this is how it can be done

app.mvcGroupManager.models.each { model ->
    // do something with model
}

5.2.2 MVCGroups and Configuration

Now that you know there are several ways to instantiate MVC groups we can go back to customizing them.

The simples way is to pass in new values as part of the arguments map that mvcGroupInit receives, for example

def group = app.mvcGroupManager.buildMVCGroup('foo', [key: 'foo'])

However is you wish to use the special config key that every MVC group configuration may have then you must instantiate the group in the following way

def configuration = app.mvcGroupManager.cloneMVCConfiguration('foo', [key: 'someValue'])
def group = configuration.create()

Note that you can still send custom arguments to the create() method.

5.2.3 Configuration Options

The following options are available to all MVC groups as long as you use the config key.

Disabling Lifecycle Events

Every MVC group triggers a few events during the span of its lifetime. These events will be sent to the event bus even if no component is interested in handling them. There may be times when you don't want these events to be placed in the event bus in order to speed up group creation/destruction. Use the following configuration to gain this effect:

mvcGroups {
    // MVC Group for "sample"
    'sample' {
        model      = 'sample.SampleModel'
        view       = 'sample.SampleView'
        controller = 'sample.SampleController'
        config {
            events {
                lifecycle = false
            }
        }
    }
}

The following events will be disabled with this setting:

Disabling Instantiation Events

The Griffon runtime will trigger an event for every artifact it manages. As with the previous events this one will be sent to the event bus even if no component handles it. Skipping publication of this event may result in a slight increase of speed during group instantiation. Use the following configuration to gain this effect:

mvcGroups {
    // MVC Group for "sample"
    'sample' {
        model      = 'sample.SampleModel'
        view       = 'sample.SampleView'
        controller = 'sample.SampleController'
        config {
            events {
                instantiation = false
            }
        }
    }
}

The following events will be disabled with this setting:

Disabling Destruction Events

This is the counterpart of the NewInstance event. Skipping publication of this event may result in a slight increase of speed when a group or any artifact instance is destroyed. Use the following configuration to gain this effect:

mvcGroups {
    // MVC Group for "sample"
    'sample' {
        model      = 'sample.SampleModel'
        view       = 'sample.SampleView'
        controller = 'sample.SampleController'
        config {
            events {
                destruction = false
            }
        }
    }
}

The following events will be disabled with this setting:

Disabling Controllers as Application Event Listeners

Controllers are registered as application event handlers by default when a group in instantiated. This makes it very convenient to have them react to events placed in the application's event bus. However you may want to avoid this automatic registration altogether, as it can lead to performance improvements. You can disable this feature with the following configuration:

mvcGroups {
    // MVC Group for "sample"
    'sample' {
        model      = 'sample.SampleModel'
        view       = 'sample.SampleView'
        controller = 'sample.SampleController'
        config {
            events {
                listener = false
            }
        }
    }
}

You can still manually register a controller as an application event handler at any time, with the caveat that it's now your responsibility to unregister it when the time is appropriate, most typically during the group's destroy sequence.