# 6. Models and Binding

This section describe models and all binding options.

## 6.1 Models

Models are very simple in nature. Their responsibility is to hold data that can be used by both Controller and View to communicate with each other. In other words, Models are not equivalent to domain classes.

Models can be observable by means of the @Bindable AST Transformation. This actually simplifies setting up bindings so that changes in the UI can automatically be sent to model properties and vice versa.

@Bindable will inject a `java.beans.PropertyChangeSupport` field and all methods required to make the model an observable class. It will also make sure that a `PropertyChangeEvent` is fired for each observable property whenever said property changes value.

The following is a list of all methods added by @Bindable

- `void addPropertyChangeListener(PropertyChangeListener listener)`
- `void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)`
- `void removePropertyChangeListener(PropertyChangeListener listener)`
- `void removePropertyChangeListener(String propertyName, PropertyChangeListener listener)`
- `PropertyChangeListener[] getPropertyChangeListeners()`
- `PropertyChangeListener[] getPropertyChangeListeners(String propertyName)`
- `void firePropertyChange(String propertyName, Object oldValue, Object newValue)`

The following is a list of all methods added by @Vetoable

- `void addVetoableChangeListener(VetoableChangeListener listener)`
- `void addVetoableChangeListener(String propertyName, VetoableChangeListener listener)`
- `void removeVetoableChangeListener(VetoableChangeListener listener)`
- `void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener)`
- `VetoableChangeListener[] getVetoableChangeListeners()`
- `VetoableChangeListener[] getVetoableChangeListeners(String propertyName)`
- `void fireVetoableChange(String propertyName, Object oldValue, Object newValue)`

Another annotation, @Listener, helps you register `PropertyChangeListeners` without so much effort. The following code

    import griffon.transform.PropertyListener
    import groovy.beans.Bindable@PropertyListener(snoopAll)
    class MyModel {
        def controller
        @Bindable String name    @Bindable
        @PropertyListener({controller.someAction(it)})
        String lastname    def snoopAll = { evt -> … }
    }

is equivalent to this one

    import groovy.beans.Bindable
    import java.beans.PropertyChangeListenerclass MyModel {
        def controller
        @Bindable String name
        @Bindable String lastname    def snoopAll = { evt -> … }    MyModel() {
            addPropertyChangeListener(snoopAll as PropertyChangeListener)
            addPropertyChangeListener('lastname', {
                controller.someAction(it)
            } as PropertyChangeListener)
        }
    }

@PropertyListener accepts the following values

- in-place definition of a closure
- reference of a closure property defined in the same class
- a List of any of the previous two

## 6.2 Binding

Binding in Griffon is achieved by leveraging Java Beans' `PropertyChangeEvent` and their related classes, thus binding will work with any class that fires this type of event, regardless of its usage of @Bindable or not.

## 6.2.1 Syntax

These are the three options for writing a binding using the `bind` node

- Long

The most complete of all three, you must specify both ends of the binding explicitly. The following snippet sets an unidirectional binding from `bean1.prop1` to `bean2.prop2`

    bind(source: bean1, sourceProperty: 'prop1',
         target: bean2, targetProperty: 'prop2')

- Contextual

This type of binding can assume either the sources or the targets depending on the context. The following snippets set an unidirectional binding from `bean1.prop1` to `bean2.prop2`

- Implicit source

    bean(bean1, prop1: bind(target: bean2, targetProperty: 'prop2'))

- Implicit target

    bean(bean2, prop2: bind(source: bean1, sourceProperty: 'prop1'))

When used in this way, either `sourceProperty:` or `targetProperty:` can be omitted; the bind node's value will become the property name, in other words

    bean(bean1, prop1: bind('prop2', target: bean2))

- Short

This type of binding is only useful for setting implicit targets. It expects a closure as the definition of the binding value

    bean(bean2, prop2: bind{ bean1.prop1 })

## 6.2.2 Additional Properties

The following properties can be used with either the long or contextual binding syntax

- mutual:

Bindings are usually setup in one direction. If this property is specified with a value of `true` then a bidirectional binding will be created instead.

    import groovy.beans.Bindable
    import groovy.swing.SwingBuilderclass MyModel {
      @Bindable String value
    }def model = new MyModel()
    def swing  = new SwingBuilder()
    swing.edt {
      frame(title: 'Binding', pack: true, visible: true) {
        gridLayout(cols: 2, rows: 3)
        label 'Normal'
        textField(columns: 20, text: bind('value', target: model))
        label 'Bidirectional'
        textField(columns: 20, text: bind('value', target: model, mutual: true))
        label 'Model'
        textField(columns: 20, text: bind('value', source: model))
      }
    }

Typing text on textfield #2 pushes the value to model, which in turns updates textfield #2 and #3, demonstrating that textfield #2 listens top model updates. Typing text on textfield #2 pushes the value to textfield #3 but not #1, demonstrating that textfield #1 is not a bidirectional binding.

- converter:

Transforms the value before it is sent to event listeners.

    import groovy.beans.Bindable
    import groovy.swing.SwingBuilderclass MyModel {
      @Bindable String value
    }def convertValue = { val ->
      '*' * val?.size()
    }def model = new MyModel()
    def swing  = new SwingBuilder()
    swing.edt {
      frame(title: 'Binding', pack: true, visible: true) {
        gridLayout(cols: 2, rows: 3)
        label 'Normal'
        textField(columns: 20, text: bind('value', target: model))
        label 'Converter'
        textField(columns: 20, text: bind('value', target: model, converter: convertValue))
        label 'Model'
        textField(columns: 20, text:  bind('value', source: model))
      }
    }

Typing text on textfield #1 pushes the value to the model as expected, which you can inspect by looking at textfield #3. Typing text on textfield #2 however transform's every keystroke into an '*' character.

- validator:

Guards the trigger. Prevents the event from being sent if the return value is `false` or `null`.

    import groovy.beans.Bindable
    import groovy.swing.SwingBuilderclass MyModel {
      @Bindable String value
    }def isNumber = { val ->
      if(!val) return true
      try {
        Double.parseDouble(val)
      } catch(NumberFormatException e) {
        false
      }
    }def model = new MyModel()
    def swing  = new SwingBuilder()
    swing.edt {
      frame(title: 'Binding', pack: true, visible: true) {
        gridLayout(cols: 2, rows: 3)
        label 'Normal'
        textField(columns: 20, text: bind('value', target: model))
        label 'Converter'
        textField(columns: 20, text: bind('value', target: model, validator: isNumber))
        label 'Model'
        textField(columns: 20, text:  bind('value', source: model))
      }
    }

You can type any characters on textfield #1 and see the result in textfield #3. You can only type numbers on textfield #2 and see the result in textfield #3

> This type of validation is not suitable for semantic validation (a.k.a. constraints in domain classes). You would want to have a look at the Validation plugin.

- sourceEvent:

Maps a different event type, instead of `PropertyChangeEvent`.

- sourceValue:

Specify a value that may come from a different source. Usually found in partnership with `sourceevent`.

    import groovy.beans.Bindable
    import groovy.swing.SwingBuilderclass MyModel {
      @Bindable String value
    }def model = new MyModel()
    def swing  = new SwingBuilder()
    swing.edt {
      frame(title: 'Binding', pack: true, visible: true) {
        gridLayout(cols: 2, rows: 3)
        label 'Text'
        textField(columns: 20, id: 'tf1')
        label 'Trigger'
        button('Copy Text', id: 'bt1')
        bind(source: bt1,
             sourceEvent: 'actionPerformed',
             sourceValue: {tf1.text},
             target: model,
             targetProperty: 'value')
        label 'Model'
        textField(columns: 20, text:  bind('value', source: model))
      }
    }

A contrived way to copy text from one textfield to another. The copy is performed by listening to `ActionEvent`s pumped by the button.