Skip to main content

JavaFX bind – Too much hype?

Posted by srikanth on June 21, 2010 at 7:19 AM PDT




 

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.

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.

Related Topics >>

Comments

Possible solutions

Other people have noticed these limitations. One tentative solution is JavaFX Binder, but I would rather prefer a more dynamic solution (avoiding compile-time code generation). But the best long-term bet is probably, getting rid of Java code. JavaFX needs standard "connector" facilities for all sorts of data sources; the JavaFX Composer from NetBeans 6.9 already contains these, but right now that's a bunch of ad-hoc classes that the composer tool throws into your project, and I don't think this feature is the most mature part of the initial Composer release. The generated code (in the form script) is a bit clumsy, I wouldn't like to write that kind of code manually; but I guess this will improve. Anyway the direction here (given by Composer) is NOT depending on Java objects, but using things similar to RowSet. This is generally a good strategy for GUIs - loose coupling from whatever back-end you have (JDBC, session beans, RESTful, etc.). Notice that the generated code doesn't yet use binding directly in the data-to-UI frontier, but it makes good use of binding internally. Just because you can't use binding everywhere, it doesn't mean that binding is not very useful.

OTOH, JavaFX's strategy depends a lot on being the ideal RIA companion to the wider Java ecosystem. Very remarkably, JavaFX must be hands-down the most productive and powerful tool to make GUIs that connect to JavaEE servers. This should be an easy card to play, as JavaFX is created by SunOracle and designed for the Java platform - unlike things like Flex or HTML5, that will always need to jump extra hoops to integrate to JavaEE back-ends. But this strategy needs that JavaFX/Java interop is smooth and seamless, as much as possible. Their common roots and shared parts help a lot, but JavaFX deviates from Java in features like binding and collections (Java's rich Collections API vs. JavaFX's jack-of-all-trades sequences). I have long promoted the RFE to add a native map type to JavaFX Script; this reduces the need of mixed-in Java code, and allows seamless interop for all collections: any Collection could be mapped to a sequence, so only Map is a problem in the current language. (The collection interop doesn't exist right now even for Collections; an assignment or cast from a List to a sequence will fail - but this could be done in principle.) Arguably you don't have to use binding just because this feature exists, but the FX toolkit doesn't support the JavaSE property change listeners either, so a FX UI that uses a Java model but avoids binding would have to resort to a programming style that's worse than [well-written] Java/Swing, without the be benefits of the Observer pattern.

Dropping support and even interop to Swing is one thing - this is still an open question, but eventually FX will be able to do everything Swing does and the ony disadvantage of poor interop (if this is not improved from 1.3) will be harder adoption by people with existing Swing assets. But the more fundamental interop between Java and JavaFX Script languages, including "system-level" APIs like Collections, is an area where I hope the FX team is still planning to make additional investment.

"will always need to jump extra hoops"

I see no problems with integrating Adobe Flex with J2EE. And HTML5 solutions could use WebSockets and REST based XML. http://www.adobe.com/devnet/livecycle/articles/blazeds_gettingstarted.html http://opensource.adobe.com/wiki/display/blazeds/BlazeDS/ http://www.adobe.com/devnet/flex/articles/flexbuilder_ws.html OpenJDK rendering is terrible, but it does work. How can JavaFX help Swing when Web Start is a Swing project ? Does the car help the bridge :P

If it is a working Swing project...

If it is a working swing code, why would you want to change it? I use Sun JDK and I dont see any issues with rendering.

If you are doing new RIA project with Java platform, then you should consider JavaFX 1.3. It is faster, better more controls and soon JFxtras 0.7 will have a bunch of controls for it. You can deploy JavaFX with WebStart or run it in Applet mode.

Since JDK 1.6_17, the dowloaded JRE is 5-6 MB in size and Sun has done a good job at it.

If your backend is java EE anyway, then you can take advantage of lot of neat things in the Java front end and gain more productivity.

-Srikanth Shenoy

Alfonso Franco

Hello dear owner, Please how can I do any help to project JavaFX to you in this moment . Iam a student at college on business programming . Firstly, I try to download the platform JavaFX with link http://javafx.com , and I could create an applet under this link that run correctly into NetBean installed platform on my computer . Please for further contact to my e-mail developfranco@yahoo.com .Thanks

JavaFX Enhancer/Wrapper for Java Objects

Thanks for the info. I did not know about the JavaFX Binder. Nor have I moved to Netbeans 6.9 yet. So I am in the dark on those fronts.

Connector does seem like a step forward - but what is really needed is this:

JavaFX language itself should support enhancer/wrapper for any Java object. From the moment a Java Object enters the javaFX world, it should automatically be enhanced and wrapped as needed. Defintiely not a JDK proxy, but more like a CGLIB style proxy. JavaFX being a superset of Java (well, mostly), this is achievable.

Notice that this functionality will JavaFX'ify any Java object and will also immediately support everything that we could ever ask for including binding and make Java object a first class citizen in JavaFX world. The connector facility that you mentioned becomes one of the specific case of this generic feature. But this feature also will provide the flexibility of doing things any way we want since it is not always about data sources. Think of an existing Java library for scientific computing combined with JavaFX visualization that could continually bind to the Java values calculated by the library and display the contour graphs!