Skip to main content

FX.js - A Smarter FXML Alternative

Posted by sanjay_dasgupta on August 3, 2012 at 1:09 AM PDT

How many lines of Java do you need to create the following JavaFX application?

HelloWorld-JavaFX

(Answer: About 30, as seen in Hello World, JavaFX Style)

And how many lines of FX.js logo (http://fxjs.java.net/) would you need?

(Answer: See below)

{T: fxStage, title: 'Hello World!',
  scene: {T: fxScene, width: 300, height: 250,
    root: {T: fxStackPane,
      children: [
        {T: fxButton, text: "Say 'Hello World'",
            onAction: function (e) {println('Hello World!');}}
      ]
    }
  }
}

Yet another new format? No, look again ... plain old JavaScript, written with attention to just a few details. If you are even slightly familiar with FXML, the details must be clear already, but if you'd like to see a tutorial, look up the FX.js logo Language Guide.

A lines-of-code comparison with the raw Java in Hello World, JavaFX Style is not really fair, so lets move on to a comparison with some full-blooded FXML. But first a quick guided tour to help you understand the example ...

But wait, there's more!

The FX.js logo script shown above has a JavaScript function labelled onAction to handle button events. But how can you have code executed at startup?

{T: fxStage, title: 'Hello World!', 
  scene: {T: fxScene, fxid: 'theScene', width: 300, height: 250,
    root: {T: fxStackPane,
      children: [
        {T: fxButton, text: "Say 'Hello World'",
            onAction: function (e) {println('Hello World!');}}
      ]
    }
  },

  main: function (args) {
    if (args.length > 0) {
      importPackage(Packages.javafx.scene.paint);
      this.theScene.setFill(Paint.valueOf(args[0]));
    }
  }
}

The main(args) function is executed at startup. It expects one command-line argument, the name of a color (that Paint.valueOf() can recognize), and sets the background of the Scene to that color. We added "fxid: 'theScene'" to the Scene object to give it a name that main() can access. The following table shows the effect of running the script with skyblue and khaki respectively as a command-line argument.

java -cp ... fxjs.Main sample.js skyblue java -cp ... fxjs.Main sample.js khaki
skyblue khaki

Incidentally, if you are a JavaScript guru frowning at the "importPackage(...);", you should take a lesson about a part of the JVM that is as threatened by neglect as its critically endangered namesake is by man's superstition and greed.

You can also add other application-specific functions and/or data alongside main() (or in place of it). Code in functions and event-handlers can access these functions and data using the "this.name" notation.

When you use FX.js logo the entire application lives in one file, no separate XML for the GUI, no Java launchers, stubs or glue files, and no build files to assemble everything. Just one warm and fuzzy ball of JavaScript. But if you need to, it's always possible to call out to Java as described in Application Architectures. You can find a bunch of FX.js logo examples in More UI Examples.

Before moving on to the FXML comparison let's quickly find out how a FX.js logo script is run.

Running a FX.js logo Script

Instructions for running a FX.js logo script can be found in the Quick Start Guide.

An FXML Comparison

Here is the FX.js logo translation of the application in Using FXML to Create a User Interface.

{T: fxStage, title: "JavaFX Welcome",
  scene: {T: fxScene, width: 300, height: 275,
    root: {T: fxGridPane, vgap: 10, hgap: 10, alignment: fxCENTER,
        padding: {T: fxInsets, top: 25, right: 25, bottom: 25, left: 25},
        stylesheets: "file:Login.css",
      children: [
        {T: fxText, id: "welcome-text", text: "Welcome",
            font: {T: fxFont, name: "Tahoma", size: 20},
            rowIndex: 0, columnIndex: 0, columnSpan: 2, rowSpan: 1},
        {T: fxLabel, text: "User Name:", rowIndex: 1, columnIndex: 0},
        {T: fxTextField, rowIndex: 1, columnIndex: 1},
        {T: fxLabel, text: "Password:", rowIndex: 2, columnIndex: 0},
        {T: fxPasswordField, rowIndex: 2, columnIndex: 1},
        {T: fxHBox, spacing: 10, rowIndex: 4, columnIndex: 1,
            alignment: fxBOTTOM_RIGHT,
            children: [{T: fxButton, text: "Sign in", onAction: function (e) {
                this.actiontarget.setFill(fxFIREBRICK);
                this.actiontarget.setText("Sign in button pressed");}}]},
        {T: fxText, fxid: "actiontarget", rowIndex: 6, columnIndex: 1},
      ]
    }
  }
}

Before trying to run this example, copy Login.css and background.jpg into the directory containing the script.

The FXML approach uses three separate files, and a number of naming and coding conventions that must be used to express the linkages between them. Which do you prefer?

The FX.js logo Project

An open-source project for FX.js logo has been started at "http://fxjs.java.net/". The interpreter, source-code, documentation, and sample applications are available at the project site. The volume of documentation and examples is still growing, but there's already enough to help you get started. Take a look!

Curious about the name?

Like many other parents (that live on my side of the planet), I consulted the recommended lists, books, astrological sites, and sundry quacks, and narrowed things down to guise and disguise. Both contained "gui", and were strangely mysterious enough to satisfy the eastern penchant for unusual names. But guise was too similar to guice. And, though the gui in disguise was a neat hint, it didn't really show that the newborn was destined to follow in the footsteps of systems that use JavaScript in unexpected ways. Long live FX.js logo!

Comments

Hi Sanjay, As the primary designer of FXML, I feel I have ...

Hi Sanjay,

As the primary designer of FXML, I feel I have to reply to your blog entry. :-)

The first thing I notice is that FXJS doesn't really look all that different from FXML. For example, here's an updated version of the Login sample that mirrors your FXJS example:

<?import javafx.geometry.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>

<?language javascript?>

<Scene width="300" height="275" stylesheets="fxml/Login.css" xmlns:fx="http://javafx.com/fxml">
  <fx:define>
    <Color fx:id="firebrick" fx:constant="FIREBRICK" />
  </fx:define>

  <GridPane alignment="center" hgap="10" vgap="10">
    <padding>
      <Insets top="25" right="25" bottom="25" left="25" />
    </padding>

    <Text id="welcome-text" text="Welcome" GridPane.columnIndex="0" GridPane.rowIndex="0"
      GridPane.columnSpan="2" />

    <Label text="User Name:" GridPane.columnIndex="0" GridPane.rowIndex="1" />
    <TextField GridPane.columnIndex="1" GridPane.rowIndex="1" />

    <Label text="Password:" GridPane.columnIndex="0" GridPane.rowIndex="2" />
    <PasswordField fx:id="passwordField" GridPane.columnIndex="1" GridPane.rowIndex="2" />

    <HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4">
      <Button text="Sign In"
        onAction="actiontarget.fill = firebrick; actiontarget.text = 'Sign in button pressed';" />
    </HBox>

    <Text fx:id="actiontarget" GridPane.columnIndex="1" GridPane.rowIndex="6" />
  </GridPane>
</Scene>

The only significant difference I see in the syntax is that the JavaScript version doesn't require a closing tag. On the other hand, XML doesn't require a comma to separate property values, something that developers can easily forget.

Some other things to consider:

  • It seems like FXJS forces developer to combine UI structure and logic. While FXML also supports this model (as shown above), it is generally discouraged. Instead, developers are encouraged to define structure in markup and logic in Java or external script files. This separation does not seem possible with FXJS.
  • FXJS appears to be limited to JavaScript. Any JVM language (including Java, Scala, Groovy, Clojure, etc.) can be used to implement the logic for FXML.
  • Many IDEs have built-in support for XML editing, making it easy for developers to work with FXML, including the Scene Builder tool which provides a GUI builder for FXML.

It also isn't clear to me if FXJS supports some of the other features of FXML like on-the-fly localization (i.e. the "%" operator) or data binding (the "${}" operator), nor is it clear if or how FXJS might support 3rd party or application-defined types (something that is very easy in FXML).

Finally, I'm curious to know how FXJS performs. Relying on a script engine for all functionality seems like it might have some negative performance implications.

I think what you have done is cool and is reminiscent of FXScript. However, I think there are definitely still numerous advantages to using FXML.

Greg

Hi Greg, Thank you for the detailed review. There is ...

Hi Greg,

Thank you for the detailed review.

There is no doubt that FXML has, and will forever retain, numerous advantages over FXJS. Apart from the fact that FXJS is just 1-month old today (and has been created in one person's spare time), FXJS is not intended to better FXML in every respect. The primary objectives are to (1) simplify learning/teaching JavaFX, (2) facilitate prototyping with JavaFX, and (3) provide an implementation vehicle when FXJS is a viable approach.

Many developers today use JavaScript as their primary language (for the web), and giving them a way of creating desktop JavaFX applications opens up new possibilities.

My documentation is evolving rapidly, and I wish you could have read some of the recent updates (particularly Java Interop, FXJS Language Guide, More UI Examples) before writing the review. But allow me to respond to your points:

Syntax (XML, etc.): The prognosis for any discussion today that compares an XML-based approach with a non-XML one is well known ;-) and I'm a little surprised that you want to bring this up. XML and JavaScript are completely different animals, do we really need to compare them?

Combined UI Structure and Logic: Not correct, see Java Interop. You can use Loader.load() from a host program in any JVM language to load a FXJS script that contains just a declarative GUI definition, and write the application's logic as native code in the host program. Exactly like the FXMLLoader.

Limited to JavaScript: Yes, as the desired intent, not a limitation. I think you are missing something when you say "Any JVM language (including Java, Scala, Groovy, Clojure, etc.) can be used to implement the logic for FXML". Scala and Groovy have user-created DSLs that support declarative JavaFX GUI definitions. Those DSLs (ScalaFX, GroovyFX) are what we should be comparing with FXML, not the the ability to "implement the logic for FXML" using procedural Scala and Groovy code. JavaScript on the JVM too can be used to procedurally implement FXML logic, but the credit for that should rightly go to Rhino, not FXJS. FXJS's claim to fame is its facility for declarative JavaFX GUI definition, and that is what the first example in the blog highlights. There is no Java DSL for declarative JavaFX GUI definition, and that is the void filled by FXML.

Built-in IDE support: The advantage that Scene Builder gives FXML is irrefutable. And FXML will remain the approach of choice for large enterprise projects for that reason alone. But that is not the same thing as built-in IDE support. IMHO generic IDE support for XML is practically useless for writing non-trivial FXML. The same goes for JavaScript IDE support -- lots of IDEs have it today and it is required for producing syntactically correct FXJS, but is nowhere near a complete solution.

Localization, data-binding, etc: This is only the first release of FXJS, mainly a showcase for the declarative GUI definition capability. Features that the FXJS user-community values will be added progressively.

Performance: FXJS allows you to write parts of your application (preferably including a declarative definition of the GUI) in JavaScript, and other parts in Java or another JVM language (see Java Interop and The Definitive Reference). For best results the FXJS developer must choose wisely from the palette of available choices.

Thank you again for your review, and I hope you have the time to keep an eye on FXJS as it evolves.

Hello to both Sanjay and Greg, Thank you - I can say that ...

Hello to both Sanjay and Greg,

Thank you - I can say that I am now a bit more intrigued than I was previously by both of these options, as well as the Java Scripting API in general.

I am beginning to think that maybe most of life's problems can in fact be solved with Java and Javascript after all (with a little help from FX and/or HTML5).