Domanda

It appears that there are two situations where assignment to a bound groovy property through @Bindable does not call the listeners:

(1) In the case where the property is assigned within the class itself, such as this.prop = newval, or, simply, prop = newval

(2) In the case where the property's value does not change obj.prop = oldval

Is there a way around this? Ideally it would support the simple (.)prop= syntax.

Code example:

import java.beans.*
import groovy.beans.*

int changes = 0
def obj = Binding.newInstance()
obj.propertyChange = { changes++ }
obj.prop = "1st change"   // change recorded
obj.twoChanges()          // no changes recorded
obj.prop = obj.prop       // no change recorded
assert changes == 4       // fails: changes is 1

class Binding {
  @Bindable String prop
  def twoChanges() {
    prop = "2nd change"
    this.prop = "3rd change"
  }
}
È stato utile?

Soluzione

By creating a hidden @Bindable property (mprop) and providing only a getter and setter for prop without an actual property named prop, I was able to use your syntax unchanged. This works for everything except where the property is set to the same value (3 out of 4 changes detected):

import java.beans.*
import groovy.beans.*

int changes = 0
def obj = Binding.newInstance()
obj.propertyChange = { changes++ }
obj.prop = "1st change"   // change recorded
obj.twoChanges()          // two changes recorded
obj.prop = obj.prop       // no change recorded
assert changes == 4       // fails: changes is 3


class Binding {
  @Bindable String mprop           // changed from prop to mprop
  def setProp(x) {setMprop(x)}     // new setter
  def getProp() {getMprop()}       // new getter
  def twoChanges() {
    prop = "2nd change"
    this.prop = "3rd change"
  }
}

Altri suggerimenti

For (1), @Bindable is an AST which generates, among other stuff, custom setters, and when you access a property inside a class, it doesn't go through the setter. This works:

import java.beans.*
import groovy.beans.*

int changes = 0
def obj = Binding.newInstance()
obj.propertyChange = { changes++ }
obj.prop = "1st change"   // change recorded
obj.twoChanges()          // no changes recorded
obj.prop = obj.prop       // no change recorded
assert changes == 3       // fails: changes is 1

class Binding {
  @Bindable String prop
  def twoChanges() {
    setProp( "2nd change" )
    this.setProp( "3rd change"  )
  }
}

For (2), well, it seems to be the standard behavior of PropertyChangeListener, since the property wasn't changed. Maybe you could supply a custom object, with a custom equals, or just create a custom setter.

Here's how I ended up doing it, without needing to use @Bindable. I'm open to suggestions, especially this.with {}.

class Obj {
  int val = 0
  static int numCalls = 0
  List assigns = []

  void setProperty(String name, Object value) {
    this.@"$name" = value
    assigns << [name: value]
    numCalls ++
  }

  int nop() {
    this.with { 
      val = val 
    }
  }

  int inc() {
    this.with { 
      this.val += 1  
    }
    return this.val
  }

}

def obj = Obj.newInstance()
assert obj.val == 0 && Obj.numCalls == 0
assert obj.inc() == 1 && obj.val == 1 && Obj.numCalls == 1
assert obj.nop() == 1 && obj.val == 1 && Obj.numCalls == 2 && obj.assigns.size() == 2
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top