FULL PRODUCT VERSION :
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
A DESCRIPTION OF THE PROBLEM :
The FXMLLoader uses the fx:id attribute to map elements defined in FXML to values of annotated fields of FXML controller classes. An fx:id is usually unique per controller (if it is not, there will be multiple assignments to the same variable). The same fx:id value - or field name - can be used by many controller classes, as it is only relevant during the loading process.
A different kind of id is the Node (CSS) id. It is used to uniquely identify a node in the scene graph. The uniqueness is not enforced, but it is the developer's responsibility to ensure that no two nodes share the same id - see the quote at the bottom.
The FXMLLoader may violate this uniqueness constraint however, by setting a node's id to the fx:id value during load. When the scene graph is constructed from the contents of multiple FXML files that contain the same fx:id (perfectly valid), the same (CSS) id will be assigned to multiple nodes in the scene graph (invalid). This will also happen when the contents of a single FXML file is loaded and added multiple times to the scene graph.
In conclusion, fx:id and node id serve different purposes. The fx:id is the link between an element definition in FXML and a variable in the controller class and is effectively unique per controller. The node id is a CSS selector and is unique per scene graph. The FXMLLoader has no way to decide whether or not an fx:id may also be used as node id. Simply setting the id to the fx:id value introduces violations of the node id's uniqueness constraint.
Quote (http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Node.html)
--
Each node in the scene graph can be given a unique id. This id is much like the "id" attribute of an HTML tag in that it is up to the designer and developer to ensure that the id is unique within the scene graph. A convenience function called lookup(String) can be used to find a node with a unique id within the scene graph, or within a subtree of the scene graph. The id can also be used identify nodes for applying styles; see the CSS section below.
--
End of quote.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a FXML file that includes a node with fx:id attribute.
2. Load the FXML twice and store the root nodes.
3. Add both root nodes to the same parent pane.
4. Perform a lookup on the parent pane, using the fx:id as selector.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Node.lookupAll() returns an empty result, because no node id was assigned by the developer.
ACTUAL -
Node.lookupAll() return two nodes, both having the same id.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
//
// DuplicateNodeIdTest.java
//
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DuplicateNodeIdTest extends Application
{
public static class Controller
{
@FXML private Button myButton;
}
@Override
public void start(Stage primaryStage) throws Exception
{
HBox hBox1 = load();
HBox hBox2 = load();
VBox vBox = new VBox(hBox1, hBox2);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
System.out.println(vBox.lookupAll("#myButton"));
}
private HBox load() throws IOException
{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Test.fxml"));
loader.setController(new Controller());
loader.load();
return loader.getRoot();
}
public static void main(String[] args)
{
launch(args);
}
}
//
// Test.fxml
//
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<HBox xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
<Button fx:id="myButton" text="Button" />
</HBox>
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
When the fx:id value should not be copied to the id, it is necessary to explicitly set the id property to an empty string, either in FXML or in plain Java code.
It is also possible to programmatically assign unique node ids to nodes that were loaded with the same fx:id.
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
A DESCRIPTION OF THE PROBLEM :
The FXMLLoader uses the fx:id attribute to map elements defined in FXML to values of annotated fields of FXML controller classes. An fx:id is usually unique per controller (if it is not, there will be multiple assignments to the same variable). The same fx:id value - or field name - can be used by many controller classes, as it is only relevant during the loading process.
A different kind of id is the Node (CSS) id. It is used to uniquely identify a node in the scene graph. The uniqueness is not enforced, but it is the developer's responsibility to ensure that no two nodes share the same id - see the quote at the bottom.
The FXMLLoader may violate this uniqueness constraint however, by setting a node's id to the fx:id value during load. When the scene graph is constructed from the contents of multiple FXML files that contain the same fx:id (perfectly valid), the same (CSS) id will be assigned to multiple nodes in the scene graph (invalid). This will also happen when the contents of a single FXML file is loaded and added multiple times to the scene graph.
In conclusion, fx:id and node id serve different purposes. The fx:id is the link between an element definition in FXML and a variable in the controller class and is effectively unique per controller. The node id is a CSS selector and is unique per scene graph. The FXMLLoader has no way to decide whether or not an fx:id may also be used as node id. Simply setting the id to the fx:id value introduces violations of the node id's uniqueness constraint.
Quote (http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Node.html)
--
Each node in the scene graph can be given a unique id. This id is much like the "id" attribute of an HTML tag in that it is up to the designer and developer to ensure that the id is unique within the scene graph. A convenience function called lookup(String) can be used to find a node with a unique id within the scene graph, or within a subtree of the scene graph. The id can also be used identify nodes for applying styles; see the CSS section below.
--
End of quote.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a FXML file that includes a node with fx:id attribute.
2. Load the FXML twice and store the root nodes.
3. Add both root nodes to the same parent pane.
4. Perform a lookup on the parent pane, using the fx:id as selector.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Node.lookupAll() returns an empty result, because no node id was assigned by the developer.
ACTUAL -
Node.lookupAll() return two nodes, both having the same id.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
//
// DuplicateNodeIdTest.java
//
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DuplicateNodeIdTest extends Application
{
public static class Controller
{
@FXML private Button myButton;
}
@Override
public void start(Stage primaryStage) throws Exception
{
HBox hBox1 = load();
HBox hBox2 = load();
VBox vBox = new VBox(hBox1, hBox2);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
System.out.println(vBox.lookupAll("#myButton"));
}
private HBox load() throws IOException
{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Test.fxml"));
loader.setController(new Controller());
loader.load();
return loader.getRoot();
}
public static void main(String[] args)
{
launch(args);
}
}
//
// Test.fxml
//
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<HBox xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
<Button fx:id="myButton" text="Button" />
</HBox>
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
When the fx:id value should not be copied to the id, it is necessary to explicitly set the id property to an empty string, either in FXML or in plain Java code.
It is also possible to programmatically assign unique node ids to nodes that were loaded with the same fx:id.