0

I've two Controllers

  1. FXMLDocumentController has TableView CustomerTable.
  2. NewCustomerController a second Controller to add a new row.

Here is my code

FXMLDocument.fxml

<AnchorPane id="AnchorPane" prefHeight="283.0" prefWidth="437.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mytableview.FXMLDocumentController">
    <children>
        <Button fx:id="button" layoutX="309.0" layoutY="25.0" onAction="#handleButtonAction" text="New Customer" />
      <TableView fx:id="customerTable" layoutX="6.0" layoutY="61.0" prefHeight="215.0" prefWidth="426.0">
        <columns>
          <TableColumn fx:id="custname" prefWidth="75.0" text="Customer Name" />
          <TableColumn fx:id="city" prefWidth="75.0" text="City" />
        </columns>
         <columnResizePolicy>
            <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
         </columnResizePolicy>
      </TableView>
    </children>
</AnchorPane>  

NewCustomer.fxml

<AnchorPane id="AnchorPane" prefHeight="172.0" prefWidth="209.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.171" fx:controller="com.newcustomer.NewCustomerController">
   <children>
      <Button layoutX="141.0" layoutY="129.0" mnemonicParsing="false" onAction="#newCustomer" text="Add" />
      <TextField fx:id="cNameTextField" layoutX="14.0" layoutY="26.0" promptText="Customer Name" />
      <TextField fx:id="custCityTextField" layoutX="14.0" layoutY="77.0" promptText="City" />
   </children>
</AnchorPane>

FXMLDocumentController.java

public class FXMLDocumentController implements Initializable {

private FXMLDocumentController documentController; //updated

@FXML
public TableView<Customer> customerTable;

@FXML
public TableColumn<Customer, String> custname;

@FXML
public TableColumn<Customer, String> city;  

@Override
public void initialize(URL url, ResourceBundle rb) { //updated
    custname.setCellValueFactory(new PropertyValueFactory<>("name"));
    city.setCellValueFactory(new PropertyValueFactory<>("city"));           
}

@FXML
private void handleButtonAction(ActionEvent event) throws IOException {
    Parent parent = FXMLLoader.load(getClass().getResource("/com/newcustomer/NewCustomer.fxml"));
    controller.setFXMLDocumentController(documentController); //updated
    Stage stage = new Stage();
    Scene scene = new Scene(parent);
    stage.setScene(scene);
    stage.show();
}

public void inflateUI(Customer customer) {
    custname.setCellValueFactory(new PropertyValueFactory<>("name"));
    city.setCellValueFactory(new PropertyValueFactory<>("city"));
    customerTable.getItems().add(customer);
}

NewCustomerController.java

public class NewCustomerController implements Initializable { 

 private FXMLDocumentController documentController; //updated

@FXML
private TextField cNameTextField;

@FXML
private TextField custCityTextField;

@Override
public void initialize(URL url, ResourceBundle rb) {
}
    public void setFXMLDocumentController(FXMLDocumentController fXMLDocumentController) { //updated
    this.documentController = fXMLDocumentController;
}

public void newCustomer(ActionEvent e) throws IOException {
    String name = cNameTextField.getText();
    String stringCity = custCityTextField.getText();

    Customer customer = new Customer(10, name, stringCity);
    FXMLLoader fXMLLoader = new FXMLLoader(getClass().getResource("/mytableview/FXMLDocument.fxml"));
    documentController = fXMLLoader.<FXMLDocumentController>getController(); //Newly updated
    documentController.inflateUI(customer); // Newly updated 
    }

}

MyTableView.java

public class MyTableView extends Application {

    public MyTableView() {
    }

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

        Scene scene = new Scene(root);

        stage.setScene(scene);
        stage.show();
    }

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

}

I want a row to be added in a TableView but it does not working.
What the exact I've missed?

Swapnil
  • 115
  • 1
  • 12
  • I tried provided questions links but they was about go to a to b controller and not reverse manner. Anyway thanks. – Swapnil Oct 08 '19 at 08:52
  • then why don't you edit your question to a) remove the bug that's already pinpointed b) demonstrates the remaining problem? Anyway, I'm off .. – kleopatra Oct 08 '19 at 08:58
  • I have provided that how much I could do. But since no more attention at question I have gave upon it. – Swapnil Oct 08 '19 at 09:04
  • 3
    rather enervating that comments get deleted all the time - they did contain useful information for potential helpers: a) the error in this code is to instantiate a new main controller manually (in CustomerController.newCustomer) thus updating a tableview that's not used b) this is a duplicate of [How to pass parameters between controllers (FXML)](https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml) which is __THE__ canonical QA for parameter passing between controllers. – kleopatra Oct 08 '19 at 11:46

1 Answers1

1

Firstly, I recommend to go through the link provided by @kleopatra to understand the concepts of communication between controllers.

Coming to your actual question, I noticed three issues in your code. Among which, one is already specified by @kleopatra.

Regarding the NPE you are getting in newCustomer() method, you initiated the FXMLLoader instance but not loading it. For this reason the getController() is null. To fix this you need to first call the load() method before calling the getController().

public void newCustomer(ActionEvent e) throws IOException {
    String name = cNameTextField.getText();
    String stringCity = custCityTextField.getText();
    Customer customer = new Customer(10, name, stringCity);
    FXMLLoader fXMLLoader = new FXMLLoader(getClass().getResource("/mytableview/FXMLDocument.fxml"));
    fXMLLoader.load(); // YOU ARE MISSING THIS LINE
    FXMLDocumentController fXMLDocumentController = fXMLLoader.<FXMLDocumentController>getController();
    fXMLDocumentController.inflateUI(customer); // Getting NPE at this line.
}

However, the above fix is USELESS because you are creating a new instance of FXMLDocumentController that is not being used (as specified by @kleopatra). You have to actually pass the controller instance to which you want to communicate. You need to create an instance variable of this controller in NewCustomerController and set to it.

@FXML
private void handleButtonAction(ActionEvent event) throws IOException {
    FXMLLoader fXMLLoader = new FXMLLoader(getClass().getResource("/com/newcustomer/NewCustomer.fxml"));
    Parent parent = fXMLLoader.load();
    NewCustomerController controller = fXMLLoader.getController();
    controller.setFXMLDocumentController(this); // Pass this controller to NewCustomerController
    Stage stage = new Stage();
    Scene scene = new Scene(parent);
    stage.setScene(scene);
    stage.show();
}

NewCustomerController.java

private FXMLDocumentController fXMLDocumentController;

public void setFXMLDocumentController(FXMLDocumentController fXMLDocumentController) {
    this.fXMLDocumentController = fXMLDocumentController;
}

public void newCustomer(ActionEvent e) throws IOException {
    String name = cNameTextField.getText();
    String stringCity = custCityTextField.getText();
    Customer customer = new Customer(10, name, stringCity);
    fXMLDocumentController.inflateUI(customer);//You are passing to the currently loaded controller
}

Lastly, you need to set the CellValueFactory only once to the TableColumns, not everytime you set the customer. You can move those two lines to initialize() method.

@Override
public void initialize(URL url, ResourceBundle rb) {
    custname.setCellValueFactory(new PropertyValueFactory<>("name"));
    city.setCellValueFactory(new PropertyValueFactory<>("city"));
}
Sai Dandem
  • 8,229
  • 11
  • 26
  • Thank you so much. I made changes as per your answer. It get rid of NPE but no changes made. – Swapnil Oct 09 '19 at 06:38
  • I understand link he provided and also I'm able to communicate from 'A' to 'B' but not revert it where 'A' is already loaded. That's my problem. That confusing me. Not understanding in reverse manner. – Swapnil Oct 09 '19 at 09:03
  • you are still instantiating a new controller in newCustomer ... be thorough when reading an answer/updating your question!! – kleopatra Oct 09 '19 at 09:56
  • Will try till I get the result. Also I'm thinking another way to do is using single controller for all of FXMLs. I also want to try this. Thank you so much – Swapnil Oct 09 '19 at 18:39
  • @kleopatra Now I've made changes in `newCustomer(){}` method removed instantiation code but still not working. – Swapnil Oct 10 '19 at 07:15
  • `controller.setFXMLDocumentController(this); // Pass this controller to NewCustomerController` I didn't understand this line. – Swapnil Oct 10 '19 at 07:53
  • compare your method line-by-line with the same method here in the answer - if you don't spot the difference (to update yours to the _exact_ same as the answer, and be it by c&p), it's impossible to help you, sry. Anyway, time for a very basic java tutorial (as I already suggested in one of the deleted comments): how do you expect to solve any problem at all without understanding basic language features? – kleopatra Oct 10 '19 at 09:22
  • @kleopatra Finally solved my problem thank you so much both of you – Swapnil Oct 10 '19 at 11:33