This is a reproduction of my java.net blog – http://weblogs.java.net/blog/srikanth/archive/2010/06/21/javafx-bind-%E2%80%93-too-much-hype

One cannot cruise through JavaFX land these days without hearing about JavaFX keywords “bind”, “inverse” and “on replace”.

In short, one could think of JavaFX bind as an Observer pattern at the language syntax level. It is one of the biggest “zing thing” purported about JavaFX in common literature. It has even been claimed that the JavaFX bind can be applied for declarative automatic data binding.

[What is data binding – In a typical application the data comes from a server. A common mechanism used in Swing based UI development is to automatically tie the data from the Java based model object to UI widgets. This mechanism is called Data Binding. JGoodies data binding is used frequently with Swing. Similarly SWT and JFace data binding is popular in Eclipse RCP world]

Having successfully used SWT/JFace data binding in the past, I was ecstatic about JavaFX providing language level bind support and decided to try it out in a real JavaFX project and found it is not all that great as hyped. [One would of course say – “Yes it has its limitations”. But what are those limitations exactly? Instead of hand waving, this blog examines them thoroughly]

Let’s start with the first and obviously a big limitation.

Limitation 1: A JavaFX attribute cannot be bound to a Java attribute.

A JavaFX attribute cannot be bound to a Java attribute. I understand there are underlying platform reasons for that, but regardless that’s a big limitation as far as I am concerned. A lot of JavaFX applications that I build are large corporate applications and my data always comes from the server as Java objects. I’d have loved to bind these Java objects to the JavaFX UI widgets – ideally bidirectional bind or at least a unidirectional bind. Both options dont work.

What makes this even more confusing is that the JavaFX compiler happily allows the unidirectional binding syntax, but does not work. However JavaFX compiler throws an explicit error that inverse binding with Java objects is not possible.

This compiles, but does not work

var firstNameTextBox:TextBox = TextBox { text: bind empJavaObj.firstName }

and this does not compile

var firstNameTextBox:TextBox = TextBox { text: bind empJavaObj.firstName with inverse }

One might think, “Hey. No problem – I will copy the data from the Java POJO (or a JPA Entity) sent by the server as a response my service call into a JavaFX presentation object. Something like this:

var firstNameTextBox:TextBox = TextBox { text: bind empFxObj.firstName with inverse }

where empFxObj is a JavaFX object representing a Employee. Well, you might be surprised that this also has its share of gotchas. That brings to me to the next limitation.

Limitation 2: A JavaFX object must be a def for bidirectional binding to work

def is sort of equivalent to Java’s final. Wow! Take a deep breath for the repercussions of this limitation to sink in. This implies that the UI widget attribute can be bound only to a constant JavaFX presentation data object. The memory reference of this JavaFX presentation data object cannot vary (but its attributes can vary). Well, for me, the whole point of data binding is to get data from the server as Java POJOs, construct a fresh JavaFX object and set it in the UI so that the data binding magic takes place. If the Java FX object has to be a constant, then all of bind effort is wasted (Remember folks – To reach this point, we have gone through the effort of copying data from the Java POJO into a JavaFX object – and this can be error prone effort especially when the returned dataset or object graph is large and involves conditional copying of data.)

Workarounds for Limitation 2

Having said that, there are two workarounds for this limitation and I did not like either. But I will mention them anyway.

Option 1: Dispose the UI every time

Since JavaFX expects the bound object to be a def, we can do this – On every invocation (or the end of it), we can throw away the UI itself and construct a fresh one. When the UI is newly constructed every time, the def is also a fresh one and so, the def is no longer a show stopper. So, if you had a controller that is listening to the UI button actions, it could simply access the def JavaFX object from the UI and make appropriate calls to the server to save the data and then construct a new UI with the freshly returned/constructed JavaFX object.

Why I don’t like Option 1

This works, but it works for simple scenarios. The whole reason one chooses JavaFX is because the UI is far more richer/complex than what can be done by traditional Web 1.0 or Web 2.0 AJAX/DOM manipulation (Very Rich Internet Applications – VRIA). This approach works like a charm for the Hello World application, but does not cut it for decently sized applications. When the UI and hence the scene graph is complex, disposing it on every server call is not a wise thing to do. [Unless of course one likes to develop slow and non-responsive application with JVM constantly garbage collecting and CPU cycles wasted in constructing new scene graphs every time ? ]

Option 2: Write some interesting code

Here is the second work around. I will show you the code snippet for the UI first.

Listing 1

public class VeryComplexUINode extends CustomNode {
var currentModel:FxEmployee on replace {
    boundModel.firstName = currentModel.firstName;
    boundModel.lastName = currentModel.lastName;
}
    def boundModel:FxEmployee = FxEmployee { lastName: ""; };
    def lastNameTxtBox:TextBox = TextBox { text: bind boundModel.lastName with inverse };
}

The VeryComplexUINode is my simplification of indeed a very complex UI node. Only one textbox is shown to illustrate the work around. Notice that the node maintains two instances of model objects. One is the boundModel and the other one is currentModel. Whenever the controller gets new data from server and creates a new JavaFX presentation object, it will set the currentModel on this node. The currentModel has an onReplace block that faithfully copies data into the boundModel. When data makes it to the boundModel, voila the UI widgets also show the latest data.

Why I don’t like Option 2

To get the JavaFX bind working, we have coded the following

  1. We copied data from the Java model object into a JavaFX presentation model object
  2. We then again copied data from the JavaFX presentation object into another

I can already hear you say: “Wait a minute!! I am jumping through hoops here to get the bidirectional syntax work. Wasn’t the goal of data binding is to prevent this kind of coding anyway?” My point exactly.

If I am copying the data from one object to another and so on just to get JavaFX bind syntax working in my scenario, I would rather not do it. Instead I would directly set the UI data manually from the Java model objects into the UI widgets.

Limitation 3: bind with inverse cannot work with expressions

Consider a case when the data to be set is based on a condition as shown below

var firstNameTextBox:TextBox = TextBox {
    text: bind if (empFxObj.lastName == null) "N/A" else empFxObj.lastName with inverse;
}

But guess what? You are not allowed to do that. Bidirectional binding don’t allow expressions. Again, I understand why it is disllowed, but this is a minor irritation while building business applications, as objects from one tier to another don’t map plain-vanilla. A transformation of data between tiers is always in the cards. [You might be wondering – why in the world would anybody not have a last name. Certain cultures don’t have last names. Closer to home, “The Artist formerly known as Prince” does not have a last name!].

NOTE:

  1. Followers of “Presentation Model is redundant v/s necessary” discussion might say that – “Hey the presentation model needs to be dumb and the transformation code needs to reside in domain to presentation model mapping tier”. You are right, this code should reside in the mapping tier, but the above code is meant to illustrate the JavaFX limitation – not a showstopper, but a nice to know limitation
  2. There is at least one case where a JavaFX presentation model is suitable. In JFxtras libraries, The XTableVIew component can only be bound to a JavaFX object. That bound JavaFX object has to extend a predefined XObject class.

Design choice: Using bind as alternative for loosely coupled Event Listeners

Now, some of you might have thought about a lot of uses for the bind syntax and I am one of them. I even thought I don’t have to write event listeners, change listeners in my system and just use bind. Well a deeper thought proved how wrong I was – The primary fact about JavaFX bind is that you know whom you are binding to. That results in a tight coupling between the source and listener. This is not an issue if the listener and source are in a same UI. There is nothing wrong about it. (In fact this is the only case where using JavaFX bind saves additional coding and is an elegant way to do things).

If you are trying to mimic an event bus sort of thing (for instance enabling the copy icon when text is selected somewhere in the UI). It can be theoretically achieved, but the code will not be pretty.

Summary

The possibilities with JavaFX bind are over-hyped. However use bind only in limited scenarios (Not many as you saw above). There are a whole lot of cases where bind seems like a good idea at first, but read this blog post for the devils in the details. The whole point of this blog post is to convince you to bid good bye to data binding in your JavaFX UI architecture using the bind syntax (at least for now and foreseeable future). It is a slippery slope that will lead you pointless customizations of your UI objects, plugging in PropertyChangeListeners in JavaFX, forcing you to invent one hack after another to support your one-off cases and duct-taping your application to prevent it from falling apart.

I recommend that you do yourself a favor and forget data binding in your application using the JavaFX bind syntax.