Reputation: 307
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
Upvotes: 1
Views: 2100
Reputation: 209684
Two approaches here.
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 ;
// ...
}
// ...
}
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.
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();
Upvotes: 3