An observable map will trigger a PropertyChangeEvent every time a value changes. We can convert a map into an observable one with the 'as' keyword too:
// don't forget the imports import java.beans.* def map = [:] as ObservableMap map.addPropertyChangeListener({ evt -> println "${evt.propertyName}: ${evt.oldValue} -> ${evt.newValue}" } as PropertyChangeListener) map.key = 'value' // prints key: null -> value map.key = 'Groovy' // prints key: value -> Groovy
We can also wrap an existing map with an ObservableMap
import java.beans.* def sorted = [a:1,b:2] as TreeMap def map = new ObservableMap(sorted) map.addPropertyChangeListener({ evt -> println "${evt.propertyName}: ${evt.oldValue} -> ${evt.newValue}" } as PropertyChangeListener) map.key = 'value' assert ['a','b','key'] == (sorted.keySet() as List) assert ['a','b','key'] == (map.keySet() as List)
Lastly we can specify a closure as an additional parameter, it will work like a filter for properties that should or should not trigger a PropertyChangeEvent when their values change, this is useful in conjunction with Expando. The filtering closure may take 2 parameters (the property name and its value) or less (the value of the property).
import java.beans.* def map = new ObservableMap({!(it instanceof Closure)}) map.addPropertyChangeListener({ evt -> println "${evt.propertyName}: ${evt.oldValue} -> ${evt.newValue}" } as PropertyChangeListener) def bean = new Expando( map ) bean.lang = 'Groovy' // prints lang: null -> Groovy bean.sayHello = { name -> "Hello ${name}" } // prints nothing, event is skipped assert 'Groovy' == bean.lang assert 'Hello Groovy' == bean.sayHello(bean.lang)






