1

Reference article Applying MVC With JavaFx

I have interface 1 FXML the following

<SplitPane>
    <items>
        <TableView prefHeight="200.0" prefWidth="200.0">
            <columns>
                <TableColumn prefWidth="75.0" text="User" />
                <TableColumn prefWidth="75.0" text="Pass" />
            </columns>
        </TableView>
        <fx:include source="Container.fxml"/>
    </items>
</SplitPane>

Container.fxml it is only used to store xml interface example I used to embed InputData.fxml,Test.fxml on TabPanel

And interface 3 InputData.xml I contains textField the username and password and a save button.

I would like to ask every time I press the button save the,interface 1 table can get value and update from the value of the interface 3

Because the interface 3 Input data.xml not embedded directly into the interface 1 it only appears when eligible.So I can not transfer data fxml directly to the following

        DataModel model = new DataModel();
        listController.initModel(model);
        editorController.initModel(model);
        menuController.initModel(model);

It on the interface if can do please help me

Community
  • 1
  • 1
Ma Tâm
  • 307
  • 4
  • 20
  • 1
    Use an MVC approach: share a model between the two controllers and make the data accessible from the model. See http://stackoverflow.com/questions/32342864/applying-mvc-with-javafx – James_D Oct 27 '16 at 16:05
  • Thanks for your detailed instructions and full source code – Ma Tâm Oct 28 '16 at 00:56
  • James_D A little greedy, but I have one more question for the topic expanded data updates update a tableview from another FXML.Looking forward to your help :D – Ma Tâm Oct 28 '16 at 16:06

1 Answers1

3

Two approaches here.

Passing the model to nested controllers

You can inject the controller from the included FXML into the controller for the including FXML using the nested controllers technique. Then the "outer controller" can propagate the model to the "nested controller".

So, e.g. if you have

<SplitPane xmlns="..." fx:controller="com.mycompany.MainController">
    <items>
        <TableView prefHeight="200.0" prefWidth="200.0">
            <columns>
                <TableColumn prefWidth="75.0" text="User" />
                <TableColumn prefWidth="75.0" text="Pass" />
            </columns>
        </TableView>
        <fx:include fx:id="container" source="Container.fxml"/>
    </items>
</SplitPane>

Suppose the controller class for Container.fxml is ContainerController. Then you would do:

public class MainController {

    private Model model ;

    @FXML
    private ContainerController containerController ; // name is fx:id with "Controller" appended

    public void setModel(Model model) {
        this.model = model ;
        containerController.setModel(model);
        // ...
    }

    // ...
}

and of course

public class ContainerController {

    private Model model ;

    public void setModel(Model model) {
        this.model = model ;
        // ...
    }

    // ...
}

Using a controller factory

If you have a lot of included FXML files and controllers, etc, this can start to get unmaintainable. In that case, a better approach might be to initialize the model in the controllers' constructors, and to use a controllerFactory on the FXMLLoader to create controllers with a model passed to the constructor. So now you controllers look like:

public class MainController {

    private final Model model ;

    public MainController(Model model) {
        this.model = model ;
    }

    public void initialize() {
        // bind controls to model here...
    }
}

and similarly for ContainerController, etc. Note this is much cleaner than the previous versions, where you had to worry about the model being set at an arbitrary time. Here you're guaranteed it is set when any code in the controller is executed.

Now you need a bit of magic for the FMXLLoader to create the controllers correctly when the FXML is loaded:

Model model = new Model();
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml"));
Callback<Class<?>, Object> controllerFactory = (Class<?> type) -> {
    try {
        for (Constructor<?> c : type.getConstructors()) {
            if (c.getParameterCount() == 1 && c.getParameterTypes()[0].equals(Model.class)) {
                return c.newInstance(model);
            }
        }
        // couldn't find constructor taking a model, just use default:
        return type.newInstance();
    } catch (Exception exc) {
        exc.printStackTrace();
        return null ;
    }
};
loader.setControllerFactory(controllerFactory);

Parent root = loader.load();
// ...

Note that FXML files loaded via an <fx:include> tag will use the same controller factory as the "including" FXML file. So this will automatically pass the same model to the nested controller.

Using DI frameworks

Finally, if you really do a lot of this, you might want to consider using a dependency inject framework to manage the dependencies on the model for you. Afterburner.fx is a dedicated JavaFX DI framework, and then everything is as simple as

public class MainController {

    @Inject 
    private Model model ;

    public void initialize() {
        // bind UI elements to model...
    }
}

You can also use Spring or Guice. E.g. with Spring, configure the controllers as prototype beans, the model as a singleton bean, and write the controllers to inject the model as with afterburner.fx. Then you can tell an FXMLLoader to use Spring to create the controllers with

// Spring application context:
ApplicationContext appContext =  ... ;
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml"));
loader.setControllerFactory(appContext::getBean);
Parent root = loader.load();
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Method 1 : So to pass values between interface 1 Main.fxml and interface 3 InputData.fxml.I need to transmit common values Model for Container.fxml For example, if I have many container xml interface: container.fxml contains container2.fxml contains InputData. I need to transfer models from Main.fxml =model-> container.fxml =model-> container2.fxml =model-> InputData.fxml Method 2 : I do not understand – Ma Tâm Oct 29 '16 at 08:35
  • 1
    @MaTâm I'm not sure I really understand the question in your last comment: you have described exactly how it should work. Each controller that needs a reference to the model has a `setModel` method. If it has a nested controller that also needs access to the model, its `setModel` method calls the nested controller's `setModel` method. You can nest this as deep as you need, but as I said it can get complicated, at which point a controller factory might be easier. – James_D Oct 29 '16 at 12:51
  • Hi James_D Thank you for your detailed instructions, I understood method 1,it will become a problem if too many nested interface.Your method 2 is very convenient but I do not know how to perform.I can create topics about method 2, urge you to continue to help http://stackoverflow.com/questions/40329861/javafx-using-a-controller-through-factory-to-update-other-xml-interface – Ma Tâm Oct 30 '16 at 14:00