The Artifact API can be a very powerful ally. Not only it's useful for adding new methods (explained in section 5.7.2 Adding Dynamic Methods at Runtime) but also comes in handy to finding out what application specific attributes an artifact has, for example Controller actions or Model properties. The following screenshot shows a simple application that presents a form based View.

When the user clicks the Submit button a dialog appears

Believe it or not both the View and the Controller know nothing about the specific property names found in the Model. Let's have a look at the Model first

package simple

@Bindable class SimpleModel { String firstName String lastName String address }

There are 3 observable properties defined in the Model. Note the usage of default imports to avoid an extra line for importing groovy.beans.Bindable. Now, the Controller on the other hand defines two actions

package simple

import griffon.util.GriffonNameUtils as GNU

class SimpleController { def model

def clear = { model.griffonClass.propertyNames.each { name -> model[name] = '' } }

@Threading(Threading.Policy.SKIP) def submit = { javax.swing.JOptionPane.showMessageDialog( app.windowManager.windows.find{it.focused}, model.griffonClass.propertyNames.collect([]) { name -> "${GNU.getNaturalName(name)} = ${model[name]}" }.join('n') ) } }

The clear() action is responsible for reseting the values of each Model property. It does so by iterating over the names of the properties found in the Model. The submit() action constructs a list fo model property names and their corresponding values, then presents it in a dialog. Notice that the Controller never refers to a Model property directly by its name, i.e, the Controller doesn't really know that the Model has a firstName property. Finally the View

package simple

import griffon.util.GriffonNameUtils as GNU

application(title: 'Simple', pack: true, locationByPlatform:true, iconImage: imageIcon('/griffon-icon-48x48.png').image, iconImages: [imageIcon('/griffon-icon-48x48.png').image, imageIcon('/griffon-icon-32x32.png').image, imageIcon('/griffon-icon-16x16.png').image]) { borderLayout() panel(constraints: CENTER, border: titledBorder(title: 'Person')) { migLayout() model.griffonClass.propertyNames.each { name -> label(GNU.getNaturalName(name), constraints: 'left') textField(columns: 20, constraints: 'growx, wrap', text: bind(name, target: model, mutual: true)) } } panel(constraints: EAST, border: titledBorder(title: 'Actions')) { migLayout() controller.griffonClass.actionNames.each { name -> button(GNU.getNaturalName(name), actionPerformed: controller."$name", constraints: 'growx, wrap') } } }

The View also iterates over the Model's property names in order to construct the form. It follows a similar approach to dynamically discover the actions that the Controller exposes.

You must install the MigLayout Plugin before running this application.