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 simpleimport griffon.util.GriffonNameUtils as GNUclass 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 simpleimport griffon.util.GriffonNameUtils as GNUapplication(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.