-1

Ok I created a tableview and there is button(ADDROW). When I press that button,a new window is open, where I fill those textfields and the information of those textfields will be added to the tableveiw, but it fails to add that information.My all methods are correct because I try to print out something on that method and it's printing those things correctly. How to fix it?

--Here are the codes

package javafxtableviewaddrows;

import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.SortedList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.stage.StageStyle;

public class FXMLDocumentController implements Initializable {

@FXML
private Label label;
@FXML private TextField filterField;
@FXML private TableView<Customer> tableview;
Button []rbutton=new Button[400];

     @FXML  public ObservableList<Customer> dataList = FXCollections.observableArrayList();    




private void calculation(){
    TableColumn id=new TableColumn("ID");
    TableColumn name=new TableColumn("NAME");
    TableColumn address=new TableColumn("ADDRESS");
    TableColumn description=new TableColumn("DESCRIPTION");
    TableColumn quantity=new TableColumn("QUANTITY");
    TableColumn transiction=new TableColumn("TRANSICTION");
    TableColumn payment=new TableColumn("PAYMENT");
    TableColumn button=new TableColumn("PREVIEW");
    
    tableview.getColumns().addAll(id,name,address,description,quantity,transiction,payment,button);
    
     for(int i=0;i<rbutton.length;i++){
        rbutton[i]=new Button();
    }
    dataList = FXCollections.observableArrayList(
           new Customer("1", "AMIT", "east west", "batman tshirt", "= 1","/NO","NOT COMPLETED",rbutton[0]),
new Customer("2", "AMIT", "east west", "batman tshirt", "= 1","/NO","NOT COMPLETED",rbutton[1]),
 new Customer("3", "AMIT", "east west", "batman tshirt", "= 1","/NO","NOT COMPLETED",rbutton[2]),
 new Customer("4", "AMIT", "east west", "batman tshirt", "= 1","/NO","NOT COMPLETED",rbutton[3])
);  
                           
    id.setCellValueFactory(new PropertyValueFactory("id"));       
    name.setCellValueFactory(new PropertyValueFactory("name"));        
    address.setCellValueFactory(new PropertyValueFactory("address"));        
    description.setCellValueFactory(new PropertyValueFactory("description"));        
    quantity.setCellValueFactory(new PropertyValueFactory("quantity")); 
    transiction.setCellValueFactory(new PropertyValueFactory("transiction")); 
    payment.setCellValueFactory(new PropertyValueFactory("payment")); 
    button.setCellValueFactory(new PropertyValueFactory("button")); 
    
    
    
    
   FilteredList<Customer> filteredData = new FilteredList<>(dataList, b -> true);
    
    // 2. Set the filter Predicate whenever the filter changes.
    filterField.textProperty().addListener((observable, oldValue, newValue) -> {
        filteredData.setPredicate(employee -> {
            // If filter text is empty, display all persons.
                            
            if (newValue == null || newValue.isEmpty()) {
                return true;
            }
            
            // Compare first name and last name of every person with filter text.
            String lowerCaseFilter = newValue.toLowerCase();
            
            if (employee.getId().toLowerCase().indexOf(lowerCaseFilter) != -1 ) {
                return true; // Filter matches first name.
            } else if (employee.getName().toLowerCase().indexOf(lowerCaseFilter) != -1) {
                return true; // Filter matches last name.
            }
            else if (employee.getPayment().toLowerCase().indexOf(lowerCaseFilter) != -1)
                 return true;
                 else  
                     return false; // Does not match.
        });
    });
    
    
    SortedList<Customer> sortedData = new SortedList<>(filteredData);
    
    
    sortedData.comparatorProperty().bind(tableview.comparatorProperty());
    
    
    tableview.setItems(sortedData);
}
public void initialize(URL url, ResourceBundle rb) {  

         calculation();  
    
}  
@FXML private void deleteRow(ActionEvent event) throws IOException{
dataList.remove(tableview.getSelectionModel().getSelectedItem());

} @FXML public void addrows(String id, String name, String address, String description, String quantity, String transiction, String payment) { Customer customer = new Customer(id, name, address, description, quantity, transiction, payment,rbutton[4]); dataList.add(customer); Platform.runLater(() -> {
    int row = tableview.getItems().size() - 1;
    tableview.getSelectionModel().select(row);
    tableview.scrollTo(row);
});

} @FXML public void addRow(ActionEvent event) throws IOException{ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("dashbord.fxml")); Parent root1 = (Parent) fxmlLoader.load(); Stage stage = new Stage(); stage.setScene(new Scene(root1));stage.show(); }

}

--Codes of the Customer class

package javafxtableviewaddrows;

import javafx.beans.property.SimpleStringProperty; import javafx.scene.control.Button;

public class Customer { private SimpleStringProperty id; private SimpleStringProperty name; private SimpleStringProperty address; private SimpleStringProperty description; private SimpleStringProperty quantity; private SimpleStringProperty transiction; private SimpleStringProperty payment; private Button button;
public Customer(String id, String name, String address, String description, String quantity, String transiction, String payment, Button button) {
    this.id = new SimpleStringProperty(id);
    this.name = new SimpleStringProperty(name);
    this.address = new SimpleStringProperty(address);
    this.description = new SimpleStringProperty(description);
    this.quantity = new SimpleStringProperty(quantity);
    this.transiction = new SimpleStringProperty(transiction);
    this.payment = new SimpleStringProperty(payment);
    this.button = button;
    this.button.setText("Preview");
}

public String getId() {
    return id.get();
}

public String getName() {
    return name.get();
}

public String getAddress() {
    return address.get();
}

public String getDescription() {
    return description.get();
}

public String getQuantity() {
    return quantity.get();
}

public String getTransiction() {
    return transiction.get();
}

public String getPayment() {
    return payment.get();
}

public Button getButton() {
    return button;
}

public void setId(String id) {
    this.id.set(id);
}

public void setName(String name) {
    this.name.set(name);
}

public void setAddress(String address) {
    this.address.set(address);
}

public void setDescription(String description) {
    this.description.set(description);
}

public void setQuantity(String quantity) {
    this.quantity.set(quantity);
}

public void setTransiction(String transiction) {
    this.transiction.set(transiction);
}

public void setPayment(String payment) {
    this.payment.set(payment);
}

public void setButton(Button button) {
    this.button = button;
}
}

--Codes of dashboardController

package javafxtableviewaddrows;

import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.TextField; import javafx.scene.paint.Color; import javafx.stage.Stage;

public class DashbordController implements Initializable {
@FXML
private TextField id;
@FXML
private TextField name;
@FXML
private TextField address;
@FXML
private TextField description;
@FXML
private TextField quantity;
@FXML
private TextField transiction;
@FXML
private TextField payment;



public void initialize(URL url, ResourceBundle rb) {
   
}    

@FXML
private void addtotable(ActionEvent event) throws IOException {
     FXMLLoader loader=new FXMLLoader (getClass().getResource("FXMLDocument.fxml"));
     Parent root=loader.load();
     FXMLDocumentController nd=loader.getController();
     
    nd.addrows(id.getText(), name.getText(), address.getText(), description.getText(), quantity.getText(), transiction.getText(), payment.getText());
   
}
}

I was expecting that the information will go to the tableview and It will display that information which I input from another stage......

  • 2
    You create a complete new `TableView` (which is never displayed) with `loader.load()` and add the data to *that* `TableView`. You need to add it to the existing `TableView`. Consider a [MVC approach](https://stackoverflow.com/questions/32342864/applying-mvc-with-javafx), or perhaps just [pass](https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml) the existing table’s items list to the `DashboardController`. – James_D Aug 06 '23 at 19:08
  • 1
    Also your `addRows(…)` method doesn’t actually do what it says it does. It simply selects the last row and scrolls to it. – James_D Aug 06 '23 at 19:09
  • @james_D what is MVC approach? – Multi Content Aug 07 '23 at 04:06
  • 3
    “what is MVC” -> There is a link in the comment and there is Google. – jewelsea Aug 07 '23 at 06:20
  • I am a beginner and I don't have much clue where to fix the codes to get the expected output.Will you help me @jewelsea – Multi Content Aug 07 '23 at 07:53
  • 2
    I did. The MVC stuff is harder to understand. But the stuff is the passing parameter link is simpler, just follow that. More assistance than that I am not prepared to provide at this time. Others might provide more specific assistance. – jewelsea Aug 07 '23 at 08:45
  • 1
    Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Community Aug 07 '23 at 11:25

2 Answers2

2

I would suggest you learn what @James_D and @jewelsea are proposing. I think these ideas lead to better programming in JavaFX. If you don't have the time to study those ideas at the moment, I would suggest using a Custom Dialog.

Main

import java.util.Optional;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class App extends Application {
   TableView<Person> table = new TableView();
   ObservableList<Person> items;
   
   Button btnAddPerson = new Button("Add Person");
   
    public static void main(String[] args) {
        App.launch(args);
    }

    @Override
    public void start(Stage stage) {        
        setupTableView();
        setupButton();
        
        HBox root = new HBox( table, btnAddPerson);
        root.setPadding(new Insets(5));
        stage.setTitle("TableView and ListView");

        Scene scene = new Scene(root, 450, 300);
        stage.setScene(scene);
        stage.show();
    }
    
    
    private void setupTableView()
    {
        // Create column UserName (Data type of String).
        TableColumn<Person, String> idCol = new TableColumn("ID");
        TableColumn<Person, String> firstNameCol = new TableColumn("First Name");
        TableColumn<Person, String> lastNameCol = new TableColumn("Last Name");
        TableColumn<Person, String> emailCol = new TableColumn("Email");
        table.getColumns().addAll(idCol, firstNameCol, lastNameCol, emailCol);

        // Get value from property of Person. .
        idCol.setCellValueFactory(cellData -> cellData.getValue().idProperty().asString());        
        firstNameCol.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
        lastNameCol.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
        emailCol.setCellValueFactory(cellData -> cellData.getValue().emailProperty());
      
        // Display row data
        items = getUserList();
        table.setItems(items);
    }
    
    private ObservableList<Person> getUserList() {
        Person user1 = new Person(1, "Susan", "smith", "smith@gmail.com");
        Person user2 = new Person(2, "Anne", "mcneil", "mcneil@gmail.com");
        Person user3 = new Person(3, "Kenvin", "white", "white@gmail.com");
        
        return FXCollections.observableArrayList(user1, user2, user3);
    }
    
    private void setupButton()
    {
        btnAddPerson.setOnAction((actionEvent) -> {
            showCustomDialog();
        });        
    }
    
    private void showCustomDialog()
    {
        // Create the custom dialog.
        Dialog<Person> dialog = new Dialog<>();
        dialog.setTitle("Add Person Dialog");
        dialog.setHeaderText("Add Person");

        // Set the icon (must be included in the project).
        //dialog.setGraphic(new ImageView(this.getClass().getResource("login.png").toString()));

        // Set the button types.
        ButtonType btntAdd = new ButtonType("Add", ButtonData.OK_DONE);
        dialog.getDialogPane().getButtonTypes().addAll(btntAdd, ButtonType.CANCEL);

        // Create the username and password labels and fields.
        GridPane grid = new GridPane();
        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(20, 150, 10, 10));

        TextField tfId = new TextField();
        TextField tfFirstName = new TextField();
        TextField tfLastName = new TextField();
        TextField tfEmail = new TextField();

        grid.add(new Label("ID:"), 0, 0);
        grid.add(tfId, 1, 0);
        grid.add(new Label("First Name:"), 0, 1);
        grid.add(tfFirstName, 1, 1);
        grid.add(new Label("Last Name:"), 0, 2);
        grid.add(tfLastName, 1, 2);
        grid.add(new Label("Email Name:"), 0, 3);
        grid.add(tfEmail, 1, 3);

        // Enable/Disable login button depending on whether a username was entered.
        Button btnAdd = (Button)dialog.getDialogPane().lookupButton(btntAdd);
        btnAdd.disableProperty().bind(
            Bindings.isEmpty(tfId.textProperty())
            .or(Bindings.isEmpty(tfFirstName.textProperty()))
            .or(Bindings.isEmpty(tfLastName.textProperty()))
            .or(Bindings.isEmpty(tfEmail.textProperty()))
        );

        dialog.getDialogPane().setContent(grid);

        // Request focus on the username field by default.
        Platform.runLater(() -> tfId.requestFocus());

        // Convert the result to a username-password-pair when the login button is clicked.
        dialog.setResultConverter(dialogButton -> {
            if (dialogButton == btntAdd) {
                return new Person(Integer.parseInt(tfId.getText()), tfFirstName.getText(), tfLastName.getText(), tfEmail.getText());
            }
            return null;
        });

        Optional<Person> result = dialog.showAndWait();
        result.ifPresent(items::add);
    }
    
    
}

Person

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Person {        
    private final IntegerProperty id;
    private final StringProperty firstName;
    private final StringProperty lastName;
    private final StringProperty email;

    public Person(int id, String fName, String lName, String email) {
        this.id = new SimpleIntegerProperty(id);
        this.firstName = new SimpleStringProperty(fName);
        this.lastName = new SimpleStringProperty(lName);
        this.email = new SimpleStringProperty(email);
    }

    public int getId()
    {
        return id.get();
    }

    public void setId(int id)
    {
        this.id.set(id);
    }

    public IntegerProperty idProperty()
    {
        return id;
    }

    public String getFirstName() {
        return firstName.get();
    }

    public void setFirstName(String fName) {
        firstName.set(fName);
    }

    public StringProperty firstNameProperty()
    {
        return firstName;
    }

    public String getLastName() {
        return lastName.get();
    }

    public void setLastName(String fName) {
        lastName.set(fName);
    }

    public StringProperty lastNameProperty()
    {
        return lastName;
    }

    public String getEmail() {
        return email.get();
    }

    public void setEmail(String fName) {
        email.set(fName);
    }

    public StringProperty emailProperty()
    {
        return email;
    }

    @Override 
    public String toString()
    {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("id: ").append(this.id.get())
                .append("\tName:").append(this.firstName.get()).append(" ").append(this.lastName.get())
                .append("\temail: ").append(this.email.get());

        return stringBuilder.toString();
    }       
}

Output

enter image description here

SedJ601
  • 12,173
  • 3
  • 41
  • 59
2

There are three ways I can see for you to approach this. I have created a greatly simplified version of the application to demonstrate each of these and focus on the three different approaches. They all use the following:

A simplified Customer class (with just one property):

package org.jamesd.examples.tableexample;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Customer {
    private final StringProperty name = new SimpleStringProperty();

    public Customer(String name){
        setName(name);
    }

    public final String getName() {
        return nameProperty().get();
    }

    public StringProperty nameProperty() {
        return name;
    }

    public final void setName(String name) {
        nameProperty().set(name);
    }
}

The main FXML file:

CustomerOverview.fxml:

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
            fx:controller="org.jamesd.examples.tableexample.CustomerOverviewController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>

    <top>
        <HBox spacing="5">
            <Label text="Search:"/>
            <TextField fx:id="searchField" onTextChange="#updateFilterText"/>
        </HBox>
    </top>
    <center>
        <TableView fx:id="customerTable">
            <columns>
                <TableColumn fx:id="nameColumn" text="Name"/>
            </columns>
        </TableView>
    </center>
    <bottom>
        <HBox spacing="5" alignment="CENTER_RIGHT">
            <Button text="Add.." onAction="#addCustomer"/>
        </HBox>
    </bottom>
</BorderPane>

and the FXML for the "Add Customer" dialog:

AddCustomerDialog.fxml

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<?import javafx.geometry.Insets?>
<GridPane xmlns="http://javafx.com/javafx"
          xmlns:fx="http://javafx.com/fxml"
          fx:controller="org.jamesd.examples.tableexample.AddCustomerDialogController"
          hgap="5"
          vgap="5">

    <padding><Insets topRightBottomLeft="10"/></padding>
    <columnConstraints>
        <ColumnConstraints hgrow="NEVER" halignment="RIGHT"/>
        <ColumnConstraints hgrow="ALWAYS" halignment="LEFT"/>
    </columnConstraints>

    <Label text="Name:" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
    <TextField fx:id="nameField" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
    <HBox GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.columnSpan="2" alignment="CENTER_RIGHT" spacing="5">
        <Button text="OK" defaultButton="true" onAction="#commit" disable="${nameField.text.blank}"/>
        <Button text="Cancel" cancelButton="true" onAction="#cancel"/>
    </HBox>
</GridPane>

The application is launched in the first two approaches in the usual way:

package org.jamesd.examples.tableexample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class CustomerApp extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(CustomerApp.class.getResource("CustomerOverview.fxml"));
        Scene scene = new Scene(fxmlLoader.load());
        stage.setTitle("Customer Overview");
        stage.setScene(scene);
        stage.show();
    }

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

Method 1: Wait for the dialog to close and then query its controller

This is the simplest approach but not really the most elegant. The basic idea is to have the controller for the dialog expose a Customer object, which is set when the user presses "OK". If the user cancels, it can be set to null.

In the controller for the main view, use showAndWait() to show the Stage containing "Add Customer" dialog. This will block execution until that stage closes, so you can then retrieve the customer.

This is basically equivalent to SedJ601's solution.

The main controller looks like this:

package org.jamesd.examples.tableexample;

import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.stage.Modality;
import javafx.stage.Stage;

import java.io.IOException;

public class CustomerOverviewController {

    @FXML
    private TextField searchField;
    @FXML
    private TableView<Customer> customerTable;
    @FXML
    private TableColumn<Customer, String> nameColumn;

    private final ObservableList<Customer> allCustomers = FXCollections.observableArrayList();
    private final FilteredList<Customer> filteredCustomers = new FilteredList<>(allCustomers);

    @FXML
    private void initialize() {
        nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());

        updateFilterText();
        SortedList<Customer> sortedCustomers = new SortedList<>(filteredCustomers);
        sortedCustomers.comparatorProperty().bind(customerTable.comparatorProperty());
        customerTable.setItems(sortedCustomers);
    }

    @FXML
    private void updateFilterText() {
        String filter = searchField.getText();
        if (filter.isBlank()) {
            filteredCustomers.setPredicate(customer -> true);
        } else {
            filteredCustomers.setPredicate(customer -> customer.getName().contains(filter));
        }
    }

    @FXML
    private void addCustomer() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("AddCustomerDialog.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root);
        Stage stage = new Stage();
        stage.setTitle("Add Customer");
        stage.setScene(scene);
        stage.initOwner(customerTable.getScene().getWindow());
        stage.initModality(Modality.WINDOW_MODAL);
        
        // showAndWait() will block execution, so code after this line is executed when the stage is closed:
        stage.showAndWait();
        
        // get the Customer the user added, if there is one, and add it to the table's items list:
        AddCustomerDialogController controller = loader.getController();
        Customer customer = controller.getCustomer();
        if (customer != null) {
            allCustomers.add(customer);
        }
    }
}

and the controller for the dialog looks like this:

package org.jamesd.examples.tableexample;

import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class AddCustomerDialogController {
    @FXML
    private TextField nameField;

    private Customer customer;
    @FXML
    private void commit() {
        String name = nameField.getText();
        if (! name.isBlank()) {
            this.customer = new Customer(nameField.getText());
        }
        closeWindow();
    }
    
    @FXML
    private void cancel() {
        this.customer = null;
        closeWindow();
    }

    private void closeWindow() {
        nameField.getScene().getWindow().hide();
    }
    public Customer getCustomer() {
        return customer;
    }
}

Method 2: Pass the table's items list to the dialog controller

This is also a fairly simple method, and it bears some relationship to MVC (see below) in the sense you are sharing data between two different controllers, one of which updates the data and one of which observes it. However, it's not a very robust solution and relies on making sure, for example, you set the list in the dialog controller before the dialog is displayed. This will work for small applications but for medium or large scale applications the code will get quite complex.

In this method, the controller for the dialog is responsible for adding the new customer to the table's items list. For that to happen, it needs a reference to the list, which is passed to that controller after the FXML is loaded.

The controller for the dialog looks like this:

package org.jamesd.examples.tableexample;

import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class AddCustomerDialogController {
    
    private ObservableList<Customer> customerList;
    @FXML
    private TextField nameField;
    
    public void setCustomerList(ObservableList<Customer> customerList) {
        this.customerList = customerList;
    }
    @FXML
    private void commit() {
        String name = nameField.getText();
        if (! name.isBlank()) {
            // Add new customer to list of customers:
            customerList.add(new Customer(nameField.getText()));
        }
        closeWindow();
    }
    
    @FXML
    private void cancel() {
        closeWindow();
    }

    private void closeWindow() {
        nameField.getScene().getWindow().hide();
    }

}

and the main controller looks like this. The only difference here is in the addCustomer() method.

package org.jamesd.examples.tableexample;

import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.stage.Modality;
import javafx.stage.Stage;

import java.io.IOException;

public class CustomerOverviewController {

    @FXML
    private TextField searchField;
    @FXML
    private TableView<Customer> customerTable;
    @FXML
    private TableColumn<Customer, String> nameColumn;

    private final ObservableList<Customer> allCustomers = FXCollections.observableArrayList();
    private final FilteredList<Customer> filteredCustomers = new FilteredList<>(allCustomers);

    @FXML
    private void initialize() {
        nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());

        updateFilterText();
        SortedList<Customer> sortedCustomers = new SortedList<>(filteredCustomers);
        sortedCustomers.comparatorProperty().bind(customerTable.comparatorProperty());
        customerTable.setItems(sortedCustomers);
    }

    @FXML
    private void updateFilterText() {
        String filter = searchField.getText();
        if (filter.isBlank()) {
            filteredCustomers.setPredicate(customer -> true);
        } else {
            filteredCustomers.setPredicate(customer -> customer.getName().contains(filter));
        }
    }

    @FXML
    private void addCustomer() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("AddCustomerDialog.fxml"));
        Parent root = loader.load();
        
        // pass the table's list to the dialog controller:
        AddCustomerDialogController customerDialogController = loader.getController();
        customerDialogController.setCustomerList(allCustomers);
        
        Scene scene = new Scene(root);
        Stage stage = new Stage();
        stage.setTitle("Add Customer");
        stage.setScene(scene);
        stage.initOwner(customerTable.getScene().getWindow());
        stage.initModality(Modality.WINDOW_MODAL);
        stage.show();
    }
}

Method 3: Use a MVC Design

Model-View-Controller (MVC) is a standard design pattern for GUI applications. This is explained a little more at Applying MVC With JavaFx. There are some variants of MVC (Model-View-Presenter and Model-View-ViewModel, for example) and in my opinion MVC with FXML usually looks a little more like MVP. I would encourage you to read up further on this and related design patterns. There are many resources available online.

The common thread to all these patterns is that the data and business logic are factored out into a separate class (the "Model"). The View typically observes the model and responds to changes in it. (JavaFX properties and observable lists are designed exactly for this purpose.) The controller/presenter/etc updates the model, which then automatically causes updates to any views that are observing it.

This approach is the most robust but needs the most learning and for you to define some additional classes. However, for medium and larger applications there is payoff for this as your controller classes and views become simpler and can just delegate a lot of work to the model. It will also make your code more consistent (fewer special cases: everything just works the same way by accessing a central model) and since it follows a standard pattern is easier for other programmers to understand.

A simple model for this application might expose the list of customers, a search filter property, and the filtered list:

package org.jamesd.examples.tableexample;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;

public class Model {
    private final ObservableList<Customer> allCustomers = FXCollections.observableArrayList();
    private final FilteredList<Customer> filteredCustomers = new FilteredList<>(allCustomers);
    private final StringProperty searchText = new SimpleStringProperty();

    public Model() {
        searchText.addListener((obs, oldText, newText) -> {
            if (newText.isBlank()) {
                filteredCustomers.setPredicate(customer -> true);
            } else {
                filteredCustomers.setPredicate(customer -> customer.getName().contains(newText));
            }
        });
    }
    
    // Convenience method, equivalent to getAllCustomers().add(customer)
    public void addCustomer(Customer customer) {
        allCustomers.add(customer);
    }

    public ObservableList<Customer> getAllCustomers() {
        return allCustomers;
    }

    public FilteredList<Customer> getFilteredCustomers() {
        return filteredCustomers;
    }

    public String getSearchText() {
        return searchText.get();
    }

    public StringProperty searchTextProperty() {
        return searchText;
    }

    public void setSearchText(String searchText) {
        this.searchText.set(searchText);
    }
}

Now if the dialog controller has a reference to the model, it can add the new customer to the list simply by calling model.addCustomer(..):

package org.jamesd.examples.tableexample;

import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class AddCustomerDialogController {

    @FXML
    private TextField nameField;

    private final Model model;

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

    @FXML
    private void commit() {
        String name = nameField.getText();
        if (! name.isBlank()) {
            Customer customer = new Customer(name);
            model.addCustomer(customer);
        }
        closeWindow();
    }

    @FXML
    private void cancel() {
        closeWindow();
    }

    private void closeWindow() {
        nameField.getScene().getWindow().hide();
    }

}

The only problem here is that this controller no longer has a no-arg constructor: its constructor requires a Model parameter. There are various ways to get around this. One is to provide a controllerFactory to the FXMLLoader. The controller factory is a function (a "Callback") which determines how to create a controller object given its class (i.e. for the class defined by the fx:controller attribute in the FXML). This doesn't really have anything to do with MVC, or this solution to the problem (though in MVC you will always need your controllers and views to have access to a shared model). But for completeness, here is a controller factory which uses some reflection to find a constructor taking a model, and invokes that constructor:

package org.jamesd.examples.tableexample;

import javafx.util.Callback;

import java.lang.reflect.Constructor;

public class ModelControllerFactory implements Callback<Class<?>, Object> {
    private final Model model;
    public ModelControllerFactory(Model model) {
        this.model = model;
    }


    // This method will be called by the FXMLLoader to create a 
    // controller instance when the FXML is loaded. The Class<?>
    // parameter that is passed into this method is determined
    // by the fx:controller attribute in the FXML. 
    //
    // The default implementation simply invokes a no-argument
    // constructor on the controller class. Since we want to
    // support controller classes with a constructor taking a
    // Model parameter, we check for such a constructor, and
    // invoke it, passing in the model instance. 
    //
    // If no such constructor exists, we revert to the default
    // implementation and call the no-arg constructor.
    
    @Override
    public Object call(Class<?> type) {
        try {
            // Search for a constructor taking one parameter of type Model:
            for (Constructor<?> c : type.getConstructors()) {
                if (c.getParameterCount() == 1 && c.getParameterTypes()[0].equals(Model.class)) {
                    // Call the constructor, passing the model:
                    return c.newInstance(model);
                }
            }
            // no constructor taking a model found: use default constructor:
            return type.getConstructor().newInstance();
        } catch (Exception exc) {
            if (exc instanceof RuntimeException re) {
                throw re;
            } else {
                throw new RuntimeException(exc);
            }
        }
    }
}

The controller for the CustomerOverview also has a reference to the model, and uses the list in the model for the table. Note how it sets a controller factory on the FXMLLoader to load the dialog FXML:

package org.jamesd.examples.tableexample;

import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.stage.Modality;
import javafx.stage.Stage;

import java.io.IOException;

public class CustomerOverviewController {

    @FXML
    private TextField searchField;
    @FXML
    private TableView<Customer> customerTable;
    @FXML
    private TableColumn<Customer, String> nameColumn;

    private final Model model;

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

    @FXML
    private void initialize() {
        nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());

        SortedList<Customer> sortedCustomers = new SortedList<>(model.getFilteredCustomers());
        sortedCustomers.comparatorProperty().bind(customerTable.comparatorProperty());
        customerTable.setItems(sortedCustomers);
    }

    @FXML
    private void updateFilterText() {
        model.setSearchText(searchField.getText());
    }

    @FXML
    private void addCustomer() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("AddCustomerDialog.fxml"));
        loader.setControllerFactory(new ModelControllerFactory(model));

        Parent root = loader.load();

        Scene scene = new Scene(root);
        Stage stage = new Stage();
        stage.setTitle("Add Customer");
        stage.setScene(scene);
        stage.initOwner(customerTable.getScene().getWindow());
        stage.initModality(Modality.WINDOW_MODAL);
        stage.show();
    }
}

Finally, since this controller also needs a model to be passed to its constructor, we need to update the application class:

public class CustomerApp extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        Model model = new Model();
        FXMLLoader fxmlLoader = new FXMLLoader(CustomerApp.class.getResource("CustomerOverview.fxml"));
        fxmlLoader.setControllerFactory(new ModelControllerFactory(model));
        Scene scene = new Scene(fxmlLoader.load());
        stage.setTitle("Customer Overview");
        stage.setScene(scene);
        stage.show();
    }

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

Note that both controllers end up with references to the same model instance. This is important, because the dialog controller must add the new Customer to the same list that the overview controller is using for the table.

James_D
  • 201,275
  • 16
  • 291
  • 322