Languages, such as JavaFX, which target a visual domain have to be event-based in order to handle the non-linearity of GUI interactions. Traditionally, in a visual programming paradigm, events are generated by components when their internal states are updated. This can require an elaborate notification-handler syntax to properly express the relationship between event broadcasters and handlers.
This section explores the easy and intuitive declarative syntax of JavaFX's event-based programming. It looks at how variable values can remain synchronized using a mechanism called binding.
This section discusses concepts that require familiarity with variable bindings as found in other scripting languages. Binding usually refers to the ability to automatically react and handle events caused by resources (or other events) to which handlers are bound.
JavaFX facilitates variable binding using the bind
keyword. Let us look at a simple example that shows the ease with which you can bind variables. Note that you can find listings for binding examples at ch01/source-code/src/binding/
.
var step = 100; def locX = bind step; println ("locX = {locX}"); step = 110; println ("locX = {locX}"); step = 150; println ("locX = {locX}");
When you run the application, you will get the following output:
locX = 100 locX = 110 locX = 150
Notice that the value of variable locX
is synchronized with the value of variable step
. Whenever step
is updated, locX
changes in value automatically.
The general syntax for binding looks like the following:
def variableX = bind expression
The idea behind binding is to keep variableX
, on the left-hand side of the assignment, updated whenever there is a change in the bound expression
on the right-hand side. JavaFX supports several forms of expressions
which can be used to update the variable on the left-hand side.
This is the simplest form of the binding syntax where the variable on the left is bound to other variables.
var x = 100; def y = bind x + 10;
When the value of variable x
changes, y
is updated with the new value of x + 10
.
JavaFX also supports conditional binding expressions, which update the left-hand side of the assignment based on a predefined condition.
var x = 2; def row = bind if((x mod 2) == 0) "even" else "odd"; for(n in [0..5]){ x = n; println ("Row {n} is {row}"); }
The value of variable row
is updated depending on the evaluation of the bound conditional expression.
Row 0 is even Row 1 is odd Row 2 is even Row 3 is odd
In the example, when the if
statement evaluates to true, row
is assigned "even", else it receives "odd".
The code block binding lets developers create compound expressions to logically control how the declared variable is updated.
var x = 2; ' var y = 2; y * x; } x = 3; println ( "X = 3, doubled = {xDoubled}"); x = 27; println ( "x = 27, doubled = {xDoubled}");
When x
is updated, the code block is re-evaluated, and xDoubled
is updated with the new value of the last expression in the block.
JavaFX can bind a variable to a function call as well.
function squareIt(x):Number { x*x; } var param = 0; def squared = bind squareIt(param); param = 96; println ("Param = {param}, squared = {squared}");
When the parameter of the function (value assigned to param)
call is updated, the function is automatically re-invoked and the variable squared
receives the newly calculated value.
When the parameter of the function (value assigned to param)
call is updated, the function is automatically re-invoked and the variable squared
receives the newly calculated value.
A variable can bind to an object literal declaration. When the values of the bound object properties change, the expression is updated with a new object.
class Location { var x:Integer; var y:Integer; } var xLoc = 0; var yLoc = 0; def loc = bind Location { x: xLoc; y: yLoc; } xLoc = 12; yLoc = 234; println ("loc.x = {loc.x}, loc.y = {loc.y}");
Note
To avoid creating a new object every time a bound property value is updated, bind each literal property in the object declaration separately as shown.
var xLoc = 0; var yLoc = 0; def loc = Location { x: bind xLoc; y: bind yLoc; }
JavaFX offers another event-based mechanism called a trigger. A trigger is a code block that gets executed when the variable it is assigned to is updated. At its simplest form, a trigger is declared as follows
def variable = value on replace [oldValueVarName]{ // code to execute }
Here, the code block is executed when the variable
on the left-hand side of the assignment is updated. The oldValueVarName
variable name is optional and holds the value of variable before the update.
The following is a simple trigger example. You can see the full code listing for this code at ch01/source-code/src/binding/TriggerDemo.fx
.
def X_BOUND = 10; var locX = 7 on replace oldX { if(locX <= X_BOUND) { println ("{oldX} ==> {locX}, in bound"); }else{ println ("{oldX} ==> {locX}, Out of bound!"); } } locX = 12; locX = 4;
Whenever the value of variable locX
is updated (including the initial assignment), the on replace
trigger is executed as well.
0 ==> 7, in bound 7 ==> 12, out of bound! 12 ==> 4, in bound