0

I am trying to make a Javafx program that will take the user input from scene one, and show it in a ListView in scene two when a button is pressed. Also, the user can go back to the scene one and add another input, and while in scene two the user can remove one of the inputs inside the listview. I have the following code, but for some reason instead of adding each new input underneath the previous one, it just overwrites the first input. Can you help me figure it out? Thanks!

In scene one I have the following code

public class Controller {

    @FXML
    private TextField userEmail;

    @FXML
    public void handleRegisterButton(ActionEvent event) throws IOException{
        Data data = Data.getInstance();
        data.setEmailAddress(userEmail.getText());

        Parent viewEmailsParent = FXMLLoader.load(getClass().getResource("../view/viewUserEmails.fxml"));
        Scene viewEmailsScene = new Scene(viewEmailsParent);

        Stage window = (Stage) ((Node)event.getSource()).getScene().getWindow();

        window.setScene(viewEmailsScene);
        window.show();

    }

}

And in scene two, where I'm trying to handle most of it, I have this code:

public class secondController implements Initializable {
    ObservableList<String> listOfEmails;

    @FXML
    ListView<String> emailList = new ListView<>();

    @FXML
    public void handleBackButton(ActionEvent event) throws IOException {
        Parent viewEmailsParent = FXMLLoader.load(getClass().getResource("../view/register.fxml"));
        Scene viewEmailsScene = new Scene(viewEmailsParent);

        Stage window = (Stage) ((Node)event.getSource()).getScene().getWindow();

        window.setScene(viewEmailsScene);
        window.show();
    }
    @FXML
    public void handleDeleteButton(ActionEvent event) throws IOException{
        String selected = emailList.getSelectionModel().getSelectedItem();
        listOfEmails.remove(selected);
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        listOfEmails = FXCollections.observableArrayList(Data.getInstance().getEmailAddress());
        emailList.setItems(listOfEmails);
    }
}

If it helps, this is the data class

public class Data {
    public static Data emailStorage;
    private String emailAddress;

    private Data(){
    }

    public static Data getInstance(){
        if(emailStorage == null){
            emailStorage = new Data();
        }
        return emailStorage;
    }

    public String getEmailAddress(){
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress){
        this.emailAddress = emailAddress;
    }
}
  • 2
    you want to pass parameters between interested parties, see https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml Also there are issues (not necessarily related to your problem): a) the resource lookup is wrong b) violating java naming conventions – kleopatra Dec 06 '20 at 15:50

1 Answers1

1

Because you reload the FXML file viewUserEmails.fxml every time, a new ListView and observable list is created every time, just displaying the single item in your data model class.

So your data model class should contain the complete list, not just the single item that was most recently added:

public class Data {
    public static Data emailStorage;
    private ObservableList<String> emailAddresses;

    private Data(){
    }

    public static Data getInstance(){
        if(emailStorage == null){
            emailStorage = new Data();
        }
        return emailStorage;
    }

    public ObservableList<String> getEmailAddresses(){
        return emailAddress;
    }

}

Now you can do:

public class SecondController implements Initializable {

    // ...

    @FXML
    ListView<String> emailList ;

    // ...

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        listOfEmails = FXCollections.observableArrayList(Data.getInstance().getEmailAddresses());
        emailList.setItems(listOfEmails);
    }

}

and

public class Controller {

    @FXML
    private TextField userEmail;

    @FXML
    public void handleRegisterButton(ActionEvent event) throws IOException{
        Data data = Data.getInstance();
        data.getEmailAddresses().add(userEmail.getText());

        // ...

    }

}

You can also consider modifying the code so that viewUserEmails.fxml is loaded only once, and redisplayed. The code above will still work with that modification.

Note there are a ton of other errors in your code, unrelated to the actual question:

  1. You should never initialize fields annotated @FXML. Note I replaced

     @FXML private ListView<String> emailList = new ListView<>();
    

    with

     @FXML private ListView<String> emailList ;
    

    If this gives you null pointer exceptions, something else is wrong

  2. Your resource paths are wrong. They will not work if you bundle this as a jar file. See How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?

  3. Using a singleton for your data model is a bad idea. (Using the singleton anti-pattern in general is a bad idea.) Instead, it's better to create an instance of your data model and pass it to the controllers that need it. See Passing Parameters JavaFX FXML

  4. Stick to Java naming conventions. It will make your code easier for other programmers to read, and aid syntax-highlighting tools to properly interpret your code.

James_D
  • 201,275
  • 16
  • 291
  • 322