Is controller a valid keyword that can be used in a fx:reference or expression binding in an FXML?

186 Views Asked by At

I defined an FXML file that contains an fx:reference element using a source attribute containing the keyword controller. Like this <fx:reference source="controller.viewModel"/>. So the fx:reference references the controller of the FXML file. And the good thing, this syntax works.

I found a few examples that are using the keyword controller in expression bindings, like this <Label text="${controller.text}"/>

But I found no documentation about this keyword controller.

I checked the documentation at http://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction_to_fxml.html. And the keyword controller is not mentioned. I only found an indication about it in the https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/FXMLLoader.html#CONTROLLER_KEYWORD

My question: is this keyword controller a feature of FXML? Or is my syntax just a hack that works? And will this hack still work in new version of the FXMLLoader?

This is my code:

ParentPane.fxml

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.TextField?>
<?import eu.primion.fxmlproject.fxmlview.ChildPane?>
<fx:root type="VBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
     <ChildPane fx:id="childPane">
       <viewModel>
         <fx:reference source="controller.viewModel"/>
       </viewModel>
     </ChildPane>
   </children>
</fx:root>

ParentPane.java

public class ParentPane extends VBox
{
    @FXML
    private ChildPane childPane;

    private final ViewModel viewModel;

    public ParentPane(final ViewModel viewModel)
    {
        this.viewModel = viewModel;

        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ParentPane.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try
        {
            fxmlLoader.load();
        }
        catch (Exception exception)
        {
            throw new RuntimeException(exception);
        }
    }

    public ViewModel getViewModel()
    {
        return this.viewModel;
    }
}

ChildPane.fxml

<fx:root type="VBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
         <TextField fx:id="textField" />
   </children>
</fx:root>

ChildPane.java

public class ChildPane extends VBox
{
    @FXML
    private TextField textField;

    private final ViewModel viewModel;

    public ChildPane(@NamedArg("viewModel") final ViewModel viewModel)
    {
        this.viewModel = viewModel;
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ChildPane.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try
        {
            fxmlLoader.load();
        }
        catch (Exception exception)
        {
            throw new RuntimeException(exception);
        }
    }

    public void initialize()
    {
      this.textField.textProperty().bindBidirectional(this.viewModel.textProperty());
    }
}
1

There are 1 best solutions below

4
On

It's not explicitly documented, but the controller is added to the FXML loader's namespace, and so it is accessible via expressions in the FXML via the expression variable controller. The same is true for the expression variables resources (which gives access to the resource bundle set on the FXML loader, or passed to the load() method) and location (which gives access to the URL representing the location of the FXML).

While this isn't explicitly documented, the FXML documentation does contain an example using this technique; so it would be extremely surprising if this were changed in a future release. In general, the behavior of a FXMLLoader is not well documented.