filippkos
filippkos

Reputation: 11

How to refresh a TableView after updating it from another scene (and another controller)?

I have a TableView that contains entries from a database and a separate window for entering a new entry with a separate controller. The problem is that when I add a record to the database, the table scene controller doesn't know that there has been a change and the TableView is only updated after the application is restarted. How to create this connection between the controllers so that the TableView is updated immediately?

Main class.

public class ExampleApplication extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("tableView.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 700, 400);
        stage.setTitle("ExampleApp");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();

    }
}

Scene controller with a table where entries are displayed.

public class TableController {

    @FXML
    private Button addEntryButton;

    @FXML
    private TableColumn<userEntry, String> entryColumn;

    @FXML
    private TableView<userEntry> table;

    ObservableList<userEntry> userEntries = FXCollections.observableArrayList();

    @FXML
    private void initialize() {
        table.getItems().clear();
        getAllEntriesFromDbToList();
        entryColumn.setCellValueFactory(new PropertyValueFactory<userEntry, String>("entryName"));
        table.setItems(userEntries);

        addEntryButton.setOnAction(event -> {
            openNewScene("/com/example/addNewEntryDialogue.fxml");
        });
    }

    private void getAllEntriesFromDbToList() {
        ResultSet rs = new DatabaseHandler().getEntriesTableFromDb();
        try {
            while (rs.next()) {
                userEntries.add(new userEntry(rs.getString("entryName")
                ));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    private void openNewScene(String window) {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource(window));

        try {
            loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Parent root = loader.getRoot();
        Stage stage = new Stage();
        stage.setScene(new Scene(root));
        stage.show();
    }

}

Dialog window controller for adding a new entry.

public class DialogueController {

    @FXML
    private Button inputNewEntryCancelButton;

    @FXML
    private Button inputNewEntryOkButton;

    @FXML
    private TextField inputNewEntryTextField;

    @FXML
    void initialize() {
        inputNewEntryOkButton.setOnAction(event -> {
            addNewEntryToDbTable(inputNewEntryTextField.getText());
            ((Node) event.getSource()).getScene().getWindow().hide();
        });

        inputNewEntryCancelButton.setOnAction(event -> {
            ((Node) event.getSource()).getScene().getWindow().hide();
        });
    }

    private void addNewEntryToDbTable(String entryName) {
        DatabaseHandler dbHandler = new DatabaseHandler();
        userEntry newEntry = new userEntry(entryName);
        dbHandler.addEntryToDb(newEntry);
    }
}

Model. Entry class.

public class userEntry {

    private String entryName;

    public userEntry(String entryName) {
        this.entryName = entryName;
    }
    
    public String getEntryName() {
        return entryName;
    }

    public void setEntryName(String entryName) {
        this.entryName = entryName;
    }
}

Model. Data base handler class.

public class DatabaseHandler extends Configs {

    Connection dbConnection;

    public Connection getDbConnection() throws ClassNotFoundException, SQLException {
        String connectionString = "jdbc:mysql://" + dbHost + ":" + dbPort + "/" + dbName;
        Class.forName("com.mysql.cj.jdbc.Driver");
        dbConnection = DriverManager.getConnection(connectionString, dbUser, dbPass);

        return dbConnection;
    }

    public void addEntryToDb(userEntry task) {
        String insert = "INSERT INTO " + Const.ENTRIES_TABLE + "(" +
                Const.ENTRIES_ENTRY_NAME + ")" +
                "VALUES(?)";
        try {
            PreparedStatement prSt = getDbConnection().prepareStatement(insert);
            prSt.setString(1, task.getEntryName());

            prSt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    public ResultSet getEntriesTableFromDb() {
        ResultSet resSet = null;
        String select = "SELECT * FROM " + Const.ENTRIES_TABLE;
        try {
            resSet = getDbConnection().createStatement().executeQuery(select);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return resSet;
    }
}

View. Table scene FXML. (tableView.fxml)

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane prefHeight="386.0" prefWidth="248.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/18" fx:controller="com.example.tableviewtesting.TableController">
   <opaqueInsets>
      <Insets />
   </opaqueInsets>
   <bottom>
        <TableView fx:id="table" maxHeight="-Infinity" prefHeight="329.0" prefWidth="248.0" BorderPane.alignment="CENTER">
            <columns>
                <TableColumn fx:id="entryColumn" prefWidth="247.0" text="Entries" />
            </columns>
        </TableView>
   </bottom>
   <top>
      <Button fx:id="addEntryButton" mnemonicParsing="false" text="Add entry">
         <BorderPane.margin>
            <Insets left="10.0" top="10.0" />
         </BorderPane.margin>
      </Button>
   </top>
</BorderPane>

View. Dialog scene FXML. (addNewEntryDialogue.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.AnchorPane?>

<AnchorPane prefHeight="100.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.tableviewtesting.DialogueController">
   <children>
      <Label alignment="CENTER" layoutX="8.0" layoutY="18.0" prefHeight="36.0" prefWidth="70.0" text="Entry name:" />
      <TextField fx:id="inputNewEntryTextField" layoutX="82.0" layoutY="16.0" prefHeight="40.0" prefWidth="208.0" />
      <Button fx:id="inputNewEntryCancelButton" layoutX="156.0" layoutY="65.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="134.0" text="Cancel" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="156.0" AnchorPane.rightAnchor="10.0" />
      <Button fx:id="inputNewEntryOkButton" layoutX="10.0" layoutY="70.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="134.0" text="Create new entry" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="156.0" />
   </children>
</AnchorPane>

Upvotes: 0

Views: 461

Answers (1)

SedJ601
SedJ601

Reputation: 13858

You did not produce an MCVE, so I am going to try to guess based on your limited code.

Here is a method that deletes a Person from an SQLite database.

The following code accepts the person that needs to be deleted in the DB and returns a boolean based on if the person was successfully deleted from the DB or not.

dBHandler.deletePerson(person)

The following code demonstrates using the boolean return value to determine what should happen based on if true or false is returned.

if (dBHandler.deletePerson(person)) {
    //delete the `Person` from the `TableView`.     
} else {
    //Alert the user that the `Person` was not deleted from the DB.
    //Do not delete the `Person` from the `TableView`.
}

Actual Code Snippet

@FXML
private void handleBtnDeletePerson(ActionEvent actionEvent) {
    Person person = tvMain.getSelectionModel().getSelectedItem();

    if (dBHandler.deletePerson(person)) {
        //Update TableView
        tvMain.getItems().remove(person); //Remove Person from the TableView
        lblLastAction.setText(person.getFirstName() + " deleted!");
    } else {
        lblLastAction.setText(person.getFirstName() + " failed to delete!");
    }

}

Full Code Link: https://github.com/sedj601/SQLitePersonTableViewExample

If you have a long-running task, see the following code: https://github.com/sedj601/SQLitePersonTableViewExampleUsingTask.

Warning: this is old code and may be lacking! Hopefully, the ideas are sound, though!

Upvotes: 1

Related Questions