#698 Data binding

tompalmer Wed 29 Jul 2009

I've thought a little now about how data binding might work for Fan. I wanted to think about it now because I'm afraid that a good solution might invalidate other work. I have simple ideas right now that might not cover everything from JavaFX, but it makes me encouraged that we might not need huge revamping to handle it. I think it fits in nicely with other discussions on bound fields and methods, too.

Here's the basic idea. If you want to watch (or bind to) objects of some type, you need to declare it @watchable (or @bindable or even mix in Watchable or Bindable?):

@watchable
class Person {
  Str name := ""
  Int age
}

Alternatively, you could perhaps mark individual fields (or perhaps methods, but that could be a trickier issue):

class Something {
  @watchable Str watchedField := ""
  Str unwatched := ""
}

It would be nice to support watching everything by default, but that also presumes you are willing to take a performance hit on all field updates. Something to consider maybe.

Behind the scenes (or maybe via a mixin), any class with watchable slots also gets methods something like the following:

Void addWatch(Slot watchedSlot, Watcher watcher)
Void removeWatch(Slot watchedSlot, Watcher watcher)

Then on GUI elements, you could have fields like so:

Void bindTo(BoundField field)
Void bindReadOnly(BoundMethod method)

And you could call them like so:

text.bindTo(BoundField(Person#name, person))

Or if we introduced binding literals:

text.bindTo(person#name)

I'm using close to current syntax, but I understand that could change. Also, feel free to change any of the wording above.

The value of binding to methods (or general Func objects?) would be that you could attach to translated values, say prices in different currencies or whatnot. But then you'd have to track all the bindable values being referenced by that function, and it could get ugly.

Anyway, note that most of this is achievable with current Fan syntax and doesn't represent a revolution in API style. That's good so it maybe doesn't need to be solved immediately, but is it too far off from JavaFX features to be sufficient?

tompalmer Wed 29 Jul 2009

And I missed the unified notification API proposal in my searching before post here. My apologies as it's obviously related.

qualidafial Wed 29 Jul 2009

text.bindTo(BoundField(Person#name, person))

This presumes that you want to bind to the Text#text, but what if I want to bind to the control's visibility, or its enablement? What if I need to convert from a non-string type e.g. DateTime, can I provide a date formatter? Can I add validation to prevent bogus data in the Text widget from being set on the model object?

I have some personal opinions based on my experience working on the Eclipse Data Binding project (EDB):

  • Properties are a very useful construct, indeed they are the most common construct. But when it comes to data binding, the observable pattern is a better fit.
  • A fully fleshed-out data binding pipeline looks like this:
    • get the source observable's value
    • validate the value (to verify it can be converted)
    • convert the value to the target observable's type
    • validate the converted value (to ensure it meets some validation criteria e.g. min/max values)
    • set the value on the target observable

When you look at all the pieces I think you'll agree this is better to implement as a system library rather than a language feature.

  • Observables are not limited to scalar values. Lists and maps can be observed too, and the notifications you get should reflect that. e.g. with lists you want to know which elements were added/updated/moved/removed. For this reason, in EDB we have different types of observables (IObservableValue, IObservableList, IObservableMap) with corresponding change event types (ValueChangeEvent, ListChangeEvent, MapChangeEvent). An event object has a source object (the observable) and a type-appropriate diff:
    • ValueDiff gives you the old and new value.
    • ListDiff represents the change as a series of indexed element additions and/or removals.
    • MapDiff tells you which keys were added, changed or removed, and lets you query the new value for any added/changed keys, and the old value for any changed/removed keys.
  • Accordingly, in EDB we also allow binding lists and sets to eachother (map binding is not yet implemented).
  • Change notification should be opt-in, otherwise we slow down the whole runtime for one language feature that a particular app may not even use.

Data binding is a complicated topic. In my mind, the only thing that Fan language could/should do for us is to have a unified notification API supported by the compiler:

class Person : Notify {
  @bound Str name
  @bound Str surname
}

which would compile to roughly:

class Person : Notify {
  @bound Str name
  {
    get;
    set
    {
      onChange.send(ValueEvent(this, #name, *name, *name=val))
    }
  }
  @bound Str surname
  {
    get;
    set
    {
      onChange.send(ValueEvent(this, #surname, *surname, *surname=val))
    }
  }
}

List and map change notifications were the basis for my proposal to introduce List.checked() and Map.checked().

tompalmer Wed 29 Jul 2009

Sounds like you have much more experience than I do in the arena. Happily, the core (whether @bound or @watchable) is very similar for our proposals.

I do also tend to agree with Stephen that the idea of instance-attached field and method literals (such as the person#name example above) is also useful. It should be one where you can get that instance object back out of it, too. The main point is to avoid pain like BoundField(Person#name, person). Sounds like Brian is leaning this way, too, and it might or might not include an & operator.

And note that "bound" is meaning two different things here: either (1) data propagation or (2) a field/method reference with a specific object as the referent.

brian Wed 29 Jul 2009

What qualidafial outlined is pretty similar to the GUI data binding design that Andy and I implemented in our past lives:

  1. data objects fire property change events
  2. these events can be bound to a UI property
  3. an arbitrary adapter algorithm is used to translate from data value (such as a number to a UI property such as a image, font, or color)

I also think some of the Linq to Events stuff is pretty interesting

Login or Signup to reply.