0

I want to reuse my JavaFX code of an add window for an update window, since they are very similar. But how can I let the view and the controller know whether it should act as an add or an update window?

This is the code from the main controller, which receives a string telling it what kind of window to set. It adjusts the title of the window accordingly:

    public static void showAddUpdateStage(int selectedTab, String addUpdate) throws IOException {
        Stage stage = new Stage();
        String[] entities = {"klant", "motorboot"};
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource(String.format("add-update-%s-view.fxml", entities[selectedTab])));
        Scene scene = new Scene(fxmlLoader.load(), 400, 200);
        String title = addUpdate.equals("add") ?
                String.format(String.format("Voeg %s toe", entities[selectedTab])) : String.format("Wijzig %s", entities[selectedTab]);
        stage.setTitle(title);
        stage.setScene(scene);
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.show();
    }

This is the code for the add/update view:

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

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<?import javafx.geometry.Insets?>
<VBox xmlns="http://javafx.com/javafx"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="nl.stijnklijn.botenverhuurjavafx.AddUpdateKlantController"
      prefHeight="400.0" prefWidth="600.0">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
    </padding>
    <GridPane alignment="CENTER" hgap="10" vgap="10">
        <Label GridPane.columnIndex="0" GridPane.rowIndex="0" text="Naam: "/>
        <TextField GridPane.columnIndex="1" GridPane.rowIndex="0" fx:id="tfNaam"/>
        <Label GridPane.columnIndex="0" GridPane.rowIndex="1" text="E-mail: "/>
        <TextField GridPane.columnIndex="1" GridPane.rowIndex="1" fx:id="tfEmail"/>
        <Label GridPane.columnIndex="0" GridPane.rowIndex="2" text="Vaarvaardig: "/>
        <CheckBox GridPane.columnIndex="1" GridPane.rowIndex="2" fx:id="chkVaarvaardig" />
    </GridPane>
    <HBox alignment="CENTER" spacing="20">
        <padding>
            <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
        </padding>
        <Button fx:id="btAdd" text="Toevoegen" onAction="#add"/>
        <Button fx:id="btCancel" text="Annuleren" onAction="#cancel"/>
    </HBox>
</VBox>

And this is the code so far for the add/update controller:

package nl.stijnklijn.botenverhuurjavafx;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import nl.stijnklijn.botenverhuurjavafx.database.DBaccess;
import nl.stijnklijn.botenverhuurjavafx.database.KlantDAO;
import nl.stijnklijn.botenverhuurjavafx.model.Klant;

import java.io.IOException;

public class AddUpdateKlantController {

    private DBaccess dBaccess;

    @FXML
    TextField tfNaam;

    @FXML
    TextField tfEmail;

    @FXML
    CheckBox chkVaarvaardig;

    @FXML
    Button btAdd;

    @FXML
    Button btCancel;

    public AddUpdateKlantController() {
        dBaccess = HelloApplication.getDBaccess();
    }

    public void add() throws IOException {
        KlantDAO klantDAO = new KlantDAO(dBaccess);
        String naam = tfNaam.getText();
        String email = tfEmail.getText();
        boolean vaarvaardig = chkVaarvaardig.isSelected();
        klantDAO.storeKlant(new Klant(naam, email, vaarvaardig));
        cancel();
        HelloApplication.showMainStage();
    }

    public void cancel() {
        Stage stage = (Stage) btCancel.getScene().getWindow();
        stage.close();
    }
}

From this controller I tried to figure out the title of the window by calling (Stage) btAdd.getScene().getWindow() in the constructor, but that doesn't work since no nodes have been initialized at that point. But I do have to set the name of the button appropriately at that point in time.

How can I do this? Thanks in advance!

  • 1
    [mcve] please .. btw: static scope of methods is nearly always the wrong tool - instead learn how to pass parameters across your application/controllers. – kleopatra Dec 11 '21 at 14:06
  • I often find that it is simpler to just have different implementations for add versus update (and especially for a non-editable view), they seem similar and have the same fields, but the logic is usually a bit different. – jewelsea Dec 11 '21 at 14:12
  • Another option would be to reuse the FXML, but define two different controller classes (one for update and one for add). Remove the `fx:controller` attribute from the FXML, and call `setController(…)` on the FXML loader, passing in whichever kind of controller you need. – James_D Dec 11 '21 at 15:50
  • I think I found a solution with which I can reuse both the FXML and the controller. Apparently, in the main controller, you can get an instance of the controller with `fxmlloader.getController`. You can then call a method in that controller which you have defined, e.g. `setupAsUpdater`, which sets up the internal variables and FXML to make it represent an updater object. I have found this to have saved me a lot of duplicate code – Stijn Klijn Dec 11 '21 at 16:37
  • 1
    Yes Stijin that is the method recommended in the passing params link. – jewelsea Dec 11 '21 at 22:41

0 Answers0