What's the Difference Between Fx:Id and Id: in Javafx

What's the difference between fx:id and id: in JavaFX?

id you use to set a CSS ID to your Component, for example <Text id="welcome-text" .../> and in your stylesheet you have something like #welcome-text { font-size: 16pt; } so this will be applied to your Text.

fx:id you use if you want to work with your Components in your Controller class, where you annotate them with @FXML Text myWelcomeText.

Determine the fx:id of a pressed button

You can get the id of the button as below:

Button btn = (Button) event.getSource();
String id = btn.getId();

An easier approach would just be to use different handlers for the buttons.

What is fx:id of JavaFX Alert OK Button?

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane fx:controller="sample.Controller" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<Button text="Click me!" fx:id="button" onAction="#clickAction" BorderPane.alignment="CENTER"/>
</BorderPane>

Assigning an fx:id value to an element, as shown in the code for the Button control, creates a variable in the document's namespace, which you can refer to from elsewhere in the code.

Using fx:id as CSS id in FXML

IMO it is partially documented in Introduction to FXML :: Variable Resolution. But not so obvious at first look:

Assigning an fx:id value to an element creates a variable in the
document's namespace that can later be referred to by variable
dereference attributes ...

Additionally, if the object's type defines
an "id" property, this value will also be passed to the objects
setId() method.

It should be completed with "if object defines the idProperty and was not set already ...". Based on the related source code is in FXMLLoader at line around 708:

        // Add the value to the namespace
if (fx_id != null) {
namespace.put(fx_id, value);

// If the value defines an ID property, set it
IDProperty idProperty = value.getClass().getAnnotation(IDProperty.class);

if (idProperty != null) {
Map<String, Object> properties = getProperties();
// set fx:id property value to Node.id only if Node.id was not
// already set when processing start element attributes
if (properties.get(idProperty.value()) == null) {
properties.put(idProperty.value(), fx_id);
}
}
...
}

I think it is ok to build on this behavior.

Reference control by fx:id

UIs like this do not lend themselves at all well to FXML. It is usually much easier, and way less code, to create a UI like this using Java. That way you can create the button and label in a loop, and add a different event handler to each:

int numDays = 30 ; // in real life this comes from the month and year
GridPane calendarPane = ...; // can be a @FXML-injected variable if needed

for (int i = 1 ; i <= numDays ; i++) {
Label dayLabel = new Label(Integer.toString(i));
Button button = new Button("None");
// set styles, etc
int day = i ;
button.setOnAction(e -> processButtonPress(button, dayLabel, day));
VBox vbox = new VBox(dayLabel, button);
int row = getRowForDay(i);
int col = getColumnForDay(i);
calendarPane.add(vbox, col, row);
}

// ...

private void handleDayChosen(Button button, Label label, int dayOfMonth) {
// whatever you need here...
label.setTextFill(Color.GREEN);
button.setText("Done");
}

Obviously you can still use FXML for the surrounding UI if you want, and just put the loop above in the controller's initialize() method. But this is clearly better than the 100+ lines of FXML plus 60 different variables in the controller to achieve the same thing.

Mismatching Javafx fx:id and variable name in controller class

Your import for ContextMenu is wrong. You have imported

com.sun.webpane.platform.ContextMenu 

where as you should have imported the context menu for javafx

javafx.scene.control.ContextMenu

What are the valid characters for a JavaFX fxml fx:id attribute?

In FXMLLoader's source code there is a method processAttribute(...) to "uumh" process attributes like fx:id, fx:controller etc. The code to validate the fx:id is:

if ( localName.equals( FX_ID_ATTRIBUTE ) )
{
// Verify that ID is a valid identifier
if ( value.equals( NULL_KEYWORD ) )
{
throw constructLoadException( "Invalid identifier." );
}

for ( int i = 0, n = value.length(); i < n; i++ )
{
if ( !Character.isJavaIdentifierPart( value.charAt( i ) ) )
{
throw constructLoadException( "Invalid identifier." );
}
}

fx_id = value;
}

so every character in fx:id is checked with Character.isJavaIdentifierPart(). The

Character.isJavaIdentifierPart('$');  // returns true
Character.isJavaIdentifierPart('>'); // returns false

Hence using > character will throw a LoadException, while $ will not.

There may not exist documentation for valid fx:id names, but at least reading the javadoc of Character.isJavaIdentifierPart() will give you some insights.

JavaFX, finding node by Id

This answer is supplemental to kleopatra's answer.

Use Java variable references instead of lookups unless you really need lookups

My advice is to avoid the CSS-based lookup methods if you can (in favor of a direct typed reference in Java).

This advice holds particularly when using FXML. FXML has the ability to assign an fx:id to a node, which can be mapped to a named, typed field in a Controller (using the @FXML annotation). In general, that is a preferred method of referencing a node by ID when FXML is involved. It is preferred for numerous reasons:

  1. When using a Java variable reference rather than a lookup, you have typed information and compile-time checks, rather than untyped runtime checks.
  2. Lookups may be time-sensitive: correct results may require a layout pass and/or CSS application.
  3. During the layout pass and rendering phases, the JavaFX framework implementation for skins on controls may add or remove various nodes which would change the result of lookups.
  4. Using lookups you may find nodes that have been added to the scene by private skin implementations. By then writing code that relies on those found nodes, you break encapsulation. A later JavaFX release might change the private skin implementation which changes the structure or nodes which are added by the skin and then breaks your code.

The above points can make it easier to code errors when using lookups rather than relying on Java variable references.

Understand the difference between an id and an fx:id

Do not confuse an fx:id used in FXML with a CSS ID, they are different things, though you might often want to assign them to be the same value, you can do that in the definition of the element in FXML, for example:

<Button fx:id="myButton" id="myButton">

The fx:id value will be used to map to a variable reference in a controller which has an @FXML annotation (or via reflection on public members if no annotation is provided):

@FXML Button myButton;

The id value will map to the id stored in the node and associated with it. The associated documentation describes it like this:

The id of this Node. This simple string identifier is useful for finding a specific Node within the scene graph. While the id of a Node should be unique within the scene graph, this uniqueness is not enforced. This is analogous to the "id" attribute on an HTML element (CSS ID Specification).

Lookups are based on CSS selectors

Here is an example lookup by ID from the node lookup documentation:

  • For example, if a Node is given the id of "myId", then the lookup method can be used to find this node as follows: scene.lookup("#myId");.

Lookups can be called on any node, or on the scene.

The node lookup(selector) methods work based on the node id, not the fx:id because the lookup string is based on css. The lookup uses a css selector. So lookup based on an ID will have a # prefix in front of the ID because that is the syntax for selecting a node via an ID. You aren't restricted to only looking up by ID. The CSS selector syntax is powerful and pretty much a language unto itself so you can have complicated expressions for querying the scene graph node tree and extracting matching nodes if you wish.

Be aware that skin implementations can modify the scene graph

If you do want to do a jquery style find (which is kind of what the JavaFX method lookup methods are), then be super careful when and how you do it due to kleopatra's second point on nodes inserted by the skin:

  • all nodes that are inserted by the skin are available only after the skin is attached, that is after showing (f.i. "#drawBoard" which is injected to the splitPane's items)

Also, note that skins can be applied in css via the fx-skin attribute of Controls. So, even the application of CSS can change the skin, which can change the scene graph which can alter the results of a lookup.

How to ensure that expected nodes will be found on lookup

To ensure that all expected nodes are in the scene to be looked up and with the expected attributes applied (e.g. size, color), etc., before doing the lookup:

First, add the required nodes to a scene.

Second, do one of the following:

  1. Generate a layout pass OR
  2. show the related stage (which will cause a layout pass) OR
  3. Do the lookup in a Platform.runLater() block.
    • This will wait for the next pulse, so the layout for the current pulse is complete before execution. Though the Platform.runLater() specification doesn't guarantee this, practically, that is how it works.

In most cases, my preferred solution would be to generate the layout pass.

To generate a layout pass:

  1. Add all required nodes to a scene with appropriate CSS styles AND
  2. Invoke the applyCss() and layout() methods on the scene root or an appropriate parent node.

Then invoke your desired lookup methods to find nodes.

Lookups without explicit layout passes (be careful)

If you know that the elements you want to lookup are already in the scene, then you can directly lookup() the nodes and they will be found. For example, if you just added them to the scene explicitly in a prior piece of code, so you know that they don't need to be created by a Skin on a subsequent pulse.

However, the looked-up nodes may not return the correct initialized values for some attributes such as height or width until after a layout has been performed. Similarly, any attributes of the looked-up nodes which are set by CSS might be incorrect until CSS has been applied to the nodes. So for safety's sake me advice would be to err on the side of caution and ensure that layout and CSS are applied to the nodes you want to look up before you try to look them up.

Summary

As might be derived from this answer the use of the JavaFX lookup() methods come with some warning and caveats. Which is the reason for the primary recommendation to avoid their use unless they add great benefit to your particular application.

And note, lookups may be of great benefit: they are very powerful, it is quite incredible what some jquery developers achieve using similar functionality, just be careful how you use them.



Related Topics



Leave a reply



Submit