0
votes

I have a scene contain a hbox(root) with 2 vboxes, each vbox contain seperate fxml files and controllers. 1st vbox contains a button and textfield,and 2nd one contain a listview,So what I need is when I click the button value on textfield should be passed to listview and update without loading a new stage thats all.
Note: I found a working solution by using

fx:include id="v1" as vbox children

in fxml and init the controllers in main controller, but sadly later if I want replace the vbox children with new fxml means, what will I do? So any simple working solutions?

Here is my maincontroller.java

public class MainController implements Initializable{
    @FXML
    private VBox v1;

    @FXML
    private VBox v2;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        try {
            v1.getChildren().add(FXMLLoader.load(getClass().getResource("voneFX.fxml")));
            v2.getChildren().add(FXMLLoader.load(getClass().getResource("vtwoFX.fxml")));
          } catch (IOException ex) {
              Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
          }
     }
}   

voneController.java

public class voneController  {

    @FXML private TextField txt1;
    @FXML private Button btn1Send;

    @FXML private void btn1SendClicked(ActionEvent event){
        //Here i would like to call updateListView method
    }
}

votwoController.java

public class vtwoController{

     @FXML
    private ListView<ListModel> sampleListview;
    ObservableList<ListModel> items = FXCollections.observableArrayList ();

    public void updateListView() {
      //This is the method which is called when user hit the button.
    }
}
1

1 Answers

0
votes

To make what you want, you need to have access to the controllers (the controllers themselves must be able to communicate with each other). FXMLLoader offers several options for accessing controllers. The first option is to automatically inject the fxml components included with the include in the fxml file.

<fx:include fx:id="childView" source="child.fxml"/>

In order for the controller to be injected, a field of the controller type specified in child.fxml must be declared with the name specified in fx:id with suffix Controller. And of course, annotate with @FXML.

public class MainController {
    @FXML
    private ChildController childViewController;

    ...
}

If you are going to be working with a dynamically changing context, you can manually create the controller and pass it on to FXMLLoader, which will initialize it by injecting the appropriate fields or simply picking up the initialized instance.

Here, it's important to first call FXMLLoader#load(), and then FXMLLoader#getController().

FXMLLoader loader = new FXMLLoader(getClass().getResource("/resource/layout/child.fxml"));
Parent parent = loader.load();

ChildController controller = loader.getController();

When manually creating the controller, you must first call FXMLLoader#setController() and then FXMLLoader#load().

ChildController controller = new ChildController();

FXMLLoader loader = new FXMLLoader(getClass().getResource("/resource/layout/child.fxml"));
loader.setController(controller);
Parent parent = loader.load();

Consequently, communication between controllers is trivial and I do not think there is a need to explain.

UPDATE

An example of how communication is made between objects. These are the two views that use the app:

child.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>

<HBox alignment="CENTER_LEFT" spacing="5.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.ChildController">
   <children>
      <Label text="Child View" />
      <TextField fx:id="textField" HBox.hgrow="ALWAYS" />
      <Button mnemonicParsing="false" text="Send to parent" onAction="#handleSendButton"/>
   </children>
</HBox>

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="5.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.MainController">
   <children>
      <HBox spacing="5.0">
         <children>
            <TextField fx:id="textField" editable="false" HBox.hgrow="ALWAYS" />
            <Button mnemonicParsing="false" text="Refresh" onAction="#handleRefreshButton"/>
         </children>
      </HBox>
      <HBox fx:id="childContainer">
         <fx:include fx:id="childView" source="child.fxml" />
      </HBox>
   </children>
</VBox>

And respectively the two controllers.

public class ChildController {

    @FXML
    private TextField textField;

    private Consumer<String> consumer;

    public void addConsumer(Consumer<String> consumer) {
        this.consumer = consumer;
    }

    @FXML
    private void handleSendButton(ActionEvent event) {
        if(consumer == null) {
            return;
        }

        consumer.accept(textField.getText());
    }

}

Here, pressing the Refresh button reloads the inserted view.

public class MainController {
    @FXML
    private TextField textField;

    @FXML
    private HBox childContainer;

    @FXML
    private ChildController childViewController;

    @FXML
    private void initialize() {
        childViewController.addConsumer(textField::setText);
    }

    @FXML
    private void handleRefreshButton(ActionEvent event) {
        try {
            textField.setText("");

            FXMLLoader loader = new FXMLLoader(getClass().getResource("child.fxml"));
            Parent parent = loader.load();

            childViewController = loader.getController();
            childViewController.addConsumer(textField::setText);

            childContainer.getChildren().setAll(parent);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}