Pass value between two JavaFX windows that are setup through fxml?

1k views Asked by At

I understand how to pass a value between two forms ala https://www.youtube.com/watch?v=HFAsMWkiLvg

The problem is in the way it is done in the video. (Being static that is). I was unable to get FXMLLoaders to work when inside of a static method because of the usage of the getClass() method. It is non-static only. getClass().getResource("myFile.fxml")

Here is how I am loading my second form.

try {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("LoginForm.fxml"));
        Parent root1 = (Parent) fxmlLoader.load();
        Stage stage = new Stage();
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.setTitle("HWI - Login");
        stage.setResizable(false);
        stage.setScene(new Scene(root1));
        stage.showAndWait();

    } catch (Exception e) {
        e.printStackTrace();
    }

Inside of scenebuilder I am setting the method to be run where it's essentially checking against a DB for a username/password. All of this is done within my loginController class. Once successful it does this. (Above I have my @FXML hook declared for loginButton)

Stage stage = (Stage) loginButton.getScene().getWindow();
stage.close();

The current way I have the program setup is that all of the menus are set to disabled before the user is signed in. I have a non-static method already setup to re-enable everything but I'm unable to call it because I can't bridge the gap between static / non-static before closing my 2nd window.

Thanks,

1

There are 1 answers

1
James_D On BEST ANSWER

The generally accepted way to share data between two or more distinct parts of the UI is to use a MVC/MVP type approach. In these patterns, you encapsulate the data in a "model" (the "M" in either) in a way that it can be observed and notifications sent if it changes. Then one part of the UI can update the model, and other parts of the UI that are observing it will be notified if it changes.

JavaFX makes this particularly easy by implementing observable properties for you, which just wrap values and make it easy to add listeners that are notified if they change.

So in this example you might do something like:

public class AuthenticationState {

    private final BooleanProperty loggedIn = new SimpleBooleanProperty(false);

    public BooleanProperty loggedInProperty() {
        return loggedIn ;
    }

    public final boolean isLoggedIn() {
        return loggedInProperty().get();
    }

    public final void setLoggedIn(boolean loggedIn) {
        loggedInProperty().set(loggedIn);
    }

    private final StringProperty userName = new SimpleStringProperty();

    public StringProperty userNameProperty() {
        return userName ;
    }

    public final String getUserName() {
        return userNameProperty().get();
    }

    public final void setUserName(String userName) {
        userNameProperty().set(userName);
    }

    // other properties as needed, e.g. IntegerProperty logInAttempts , etc.
}

Now your main controller can do:

public class MainController {

    @FXML
    private final MenuItem deleteAllDataMenuItem ;

    private AuthenticationState authenticationState ;

    public void initialize() {

        authenticationState = new AuthenticationState();

        deleteAllDataMenuItem.disableProperty()
            .bind(authenticationState.loggedInProperty().not());

    }

    @FXML
    public void logIn() {
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("LoginForm.fxml"));
            Parent root1 = (Parent) fxmlLoader.load();

            LoginController loginController = fxmlLoader.getController();
            loginController.setAuthenticationState(authenticationState);

            Stage stage = new Stage();
            stage.initModality(Modality.APPLICATION_MODAL);
            stage.setTitle("HWI - Login");
            stage.setResizable(false);
            stage.setScene(new Scene(root1));
            stage.showAndWait();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

and your login controller can look like:

public class LoginController {

    private AuthenticationState authenticationState ;

    public void setAuthenticationState(AuthenticationState authenticationState) {
        this.authenticationState = authenticationState ;
    }

    @FXML
    public void login() {
        // check login:
        boolean loginSuccessful = ... ;

        authenticationState.setLoggedIn(loginSuccessful);

        // ...
    }
}

Now when the user logs in, the login controller sets the loggedIn property in the authenticationState to true. Since the disabled state of the menu item is bound to (the negative of) the loggedIn property, the menu item is automatically enabled. If you have a "Log Out" button, just have it set the loggedIn property to false, and the menu item will be disabled again.