FXML Controllers
State
An FXML controller should virtually never have static state (i.e., no static
fields). Each time you load an FXML file, a new instance of its associated controller class is created. If you have static fields, then that state is shared across all instances of the controller class, and that doesn't make sense conceptually.
Communication between controllers
Communicating between controllers should rarely, if ever, be done directly. Instead, you should be using a proper application architecture like Model-View-Controller (MVC) or Model-View-ViewModel (MVVM). Then communication between the different controllers, and thus views, is done by interacting with the model. The model would be observable in some way so that state changes in the model are reflected in other views as appropriate. The only exception to this is in the context of nested FXML files. But even then, it would probably be best to only inject the nested controller to pass it an instance of a shared model, then leave further communication to be done via said model.
Note an FXML controller is not a "controller" in the MVC sense. Using MVC is a "higher level" concept. Besides, the FXML controller functions more like a "presenter" from the Model-View-Presenter (MVP) architecture.
See Q&As like Applying MVC With JavaFx for more information. Also look for tutorials, articles, books, etc. on the concepts in general.
Inject Nested Controller
If you want access to the nested FXML file's controller instance, then you should inject it into the "parent" controller similar to any other FXML component. That is to say, give the fx:include
element an fx:id
attribute, then define an appropriate field in the controller. For instance, if you had:
<!--
Where the root element of 'Foo.fxml' is a StackPane and the
controller is an instance of 'FooController'
-->
<fx:include fx:id="foo" source="Foo.fxml"/>
Then in the controller you'd have:
// to inject root element of nested FXML file (if needed)
@FXML private StackPane foo;
// to inject nested FXML file's controller instance (if needed)
@FXML private FooController fooController; // note field name is '<fx:id>Controller'
Note a nested controller will be fully initialized before the "parent" controller is initialized.
Controller Initialization
The nested controller in your question has the following method:
@FXML
public void initialize(URL location, ResourceBundle resources)
But the class does not implement javafx.fxml.Initializable
. That means this initialize
method is not actually being invoked.
You should have either:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
public class Controller implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
// ...
}
}
Or:
import java.net.URL; // omit if not needed
import java.util.ResourceBundle; // omit if not needed
import javafx.fxml.FXML;
public class Controller {
@FXML private URL location; // inject location; omit if not needed
@FXML private ResourceBundle resources; // inject resources; omit if not needed
@FXML
private void initialize() { // a no-argument 'initialize' method
// ...
}
}
Note the latter approach—the one that does not implement Initializable
—is the preferred approach.
If your controller does not need any additional initialization, then you can omit the initialize
method entirely.
Demonstration
This demo only shows injecting a nested controller and calling methods on it and the objects it contains. It does not show how to use an architecture like MVC.
The demo has the nested controller initialize a "model" object that simply has an integer property. Said nested controller increments this property every time the button is fired. The "parent" controller goes through this "model" object to get the property and binds a label's text property to it.
Note this example expects the FXML resources to be at the root of the class-path/module-path (i.e., in the unnamed/default package).
Code
module-info.java (only needed if code is modular):
module app {
requires javafx.controls;
requires javafx.fxml;
exports com.example.app to javafx.graphics;
opens com.example.app to javafx.fxml;
}
Main.java:
package com.example.app;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(Main.class.getResource("/Main.fxml"));
primaryStage.setScene(new Scene(root, 500, 300));
primaryStage.show();
}
}
MainController.java:
package com.example.app;
import javafx.beans.binding.StringBinding;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class MainController {
@FXML private NestedController nestedController; // inject nested controller
@FXML private Label label;
@FXML
private void initialize() {
// print statement to show order of controller initialization
System.out.println("Initializing main controller...");
// Show use of nested controller during initialization of "parent" controller
StringBinding binding = nestedController
.getModel()
.countProperty()// access attribute of "another class"
.asString("You clicked the button %,d times(s)!");
label.textProperty().bind(binding);
}
}
Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.StackPane?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.app.MainController">
<padding>
<Insets topRightBottomLeft="10"/>
</padding>
<center>
<StackPane>
<Label fx:id="label"/>
</StackPane>
</center>
<bottom>
<fx:include fx:id="nested" source="Nested.fxml"/>
</bottom>
</BorderPane>
NestedController.java:
package com.example.app;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
public class NestedController {
private final ClickCountModel model = new ClickCountModel();
public ClickCountModel getModel() {
return model;
}
@FXML
private void initialize() {
// print statement to show order of controller initialization
System.out.println("Initializing nested controller...");
}
@FXML
private void handleAction(ActionEvent event) {
event.consume();
model.incrementCount();
}
}
Nested.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.StackPane?>
<StackPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.app.NestedController">
<Button text="Click me!" onAction="#handleAction"/>
</StackPane>
ClickCountModel.java:
package com.example.app;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class ClickCountModel {
private final IntegerProperty count = new SimpleIntegerProperty(this, "count");
public final void setCount(int count) { this.count.set(count); }
public final int getCount() { return count.get(); }
public final IntegerProperty countProperty() { return count; }
public void incrementCount() {
setCount(getCount() + 1);
}
}
In Action
GIF:

Console output:
Initializing nested controller...
Initializing main controller...