How do I access a UI element from another controller class in JavaFX?

fxml, java, java-8, javafx

Solution

When you load the FXML for the secondary controller, do this:

FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("second.fxml"));
AnchorPane frame = fxmlLoader.load();
FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();

Then you can pass references to the second controller. This could just be the TextArea.

EDIT:

Replaced the `load()` call in the snippet above and added the `setLocation()` call. The old line `AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml"));` was wrong as it called the static `load` function, which is useless here.

EDIT:

(Changed the code snippet above to better match your variable names.) The code snippet above replaces this part of your code:

AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));

That line uses the FXMLLoader to load the view and also creates the instance of the controller - in this case `FXMLSecondaryController`. However, you cannot use the static `FXMLLoader.load` method for that, you need an instance of `FXMLLoader`. That instance holds a reference to the controller after load and you can retrieve that with `getController()`. And you need to cast the returned value to your controller class (with `(FXMLSecondaryController)`).

Your primary controller has a field:

@FXML private TextArea myArea;

This holds a reference to your `TextArea` and is initialized by the fxml loader. (If you remove the `@FXML` annotation, the loader won't touch it.)

In your secondary controller, add this field:

public TextArea primaryTextArea;

Note that `@FXML` is gone (the fxml loader shall not touch the field) as well as `private` (you want to set the field, hence others must see it). Now you can set this field after the controller has been loaded. So back to the loading code:

FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("second.fxml"));
AnchorPane frame = fxmlLoader.load();
FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();
// Add this too:
c.primaryTextArea = myArea;

EDIT: Replaced the `load()` call in the snippet above and added the `setLocation()` call. The old line `AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml"));` was wrong as it called the static `load` function, which is useless here.

The `FXMLSecondaryController` now has a reference to the `TextArea` of the primary controller. In one of its methods you should be able to access its content:

public class FXMLSecondaryController implements Initializable {
    // ...

    public TextArea primaryTextArea;

    // ...

    @FXML
    private void doSomething(ActionEvent event) throws Exception {
        primaryTextArea.appendText("Hi ho!");
    }

}

Note that this solution is not the best approach, but it is simple and can serve as a start. Using binding is certainly recommended. I would try to create a class which holds the data and on which the other controllers bind. But if you take the simple approach for now, it should be sufficient.

Problem

I have a JavaFX / Java 8 application written with NetBeans 8 (no `SceneBuilder`). My application has a main window that has its own FXML file (primary.fxml) and its own controller class (FXMLPrimaryController.java). One of the items in the FXML is a `TextArea`. Some of the methods in FXMLPrimaryController.java are about appending to that `TextArea`. This application now spawns a second window (another "stage") with its own FXML (second.fxml) and its own controller class (FXMLsecondController.java). Within the second controller class, how can I access the TextArea in the primary? Here's a sample of the relevant code: primary.fxml: ``` <Button text="press me!" onAction="#openSecondWindow" /> <TextArea fx:id="myArea" /> ``` FXMLPrimaryController.java: ``` public class FXMLPrimaryController implements Initializable { @Override public void initialize(URL url, ResourceBundle rb) { } @FXML private TextArea myArea; final public void writeToTextArea() { myArea.appendText("hi!"); } @FXML private void openSecondWindow(ActionEvent event) throws Exception { Group root = new Group(); Stage stage = new Stage(); AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml")); root.getChildren().add(frame); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } } ``` There is nothing fancy about second.fxml. Assume there is a button with `onAction="#writeSomething"`. In FXMLsecondController.java, I would like a function that references the above `TextArea`.

Original source