JFoenix icon indicating copy to clipboard operation
JFoenix copied to clipboard

How to use JFXAlerts?

Open TurekBot opened this issue 7 years ago • 9 comments

There's no place for JFXAlerts in the demo, yet! Allow me to take the first step.

TurekBot avatar Mar 07 '18 17:03 TurekBot

Here, now they have a place—all the boilerplate is done. #634

But there's nothing "demonstrated" yet. Can anyone help out with that?

TurekBot avatar Mar 07 '18 17:03 TurekBot

Use JFXDialog instead of alerts.

alamenai avatar Mar 19 '18 22:03 alamenai

Just for clarification, @XlintXms this issue is a request for JFXAlert documentation.

jfoenixadmin avatar Mar 20 '18 15:03 jfoenixadmin

@jfoenixadmin, I saw that you added an example of how to use a JFXAlert; thanks :+1: .

You mentioned in #634 that "unlike java Alerts, JFXAlert doesn't support return type."

It'll be important to make it clear in the documentation how users should go about getting a result after a JFXAlert closes.

If one tries to use a JFXAlert like a Dialog things don't work the same. It's quite confusing.

While

Optional<String> result = dialog.showAndWait();
result.ifPresent(string -> System.out.println(string));

would give you back the right result when using a Dialog, with a JFXAlert you'll always get back a ButtonData.CANCEL_CLOSE.

TurekBot avatar Mar 26 '18 19:03 TurekBot

If I understand correctly, the reason for this is because JFXAlert doesn't use ButtonTypes. Through a series of events native to Dialog, the ButtonData.CANCEL_CLOSE is returned because no result is specified.

I may have found a way to work around this. Say we don't use ButtonTypes, still. But we do set the result in the setOnAction method of the buttons we add to the actions section.

An example:

JFXTextField usernameTextField = new JFXTextField();

addUserButton.setOnAction(event -> {
	// Ensure that the user can't close the alert.
	JFXAlert<String> alert = new JFXAlert<>((Stage) addUserButton.getScene().getWindow());
	alert.initModality(Modality.APPLICATION_MODAL);
	alert.setOverlayClose(false);
	
	// Create the content of the JFXAlert with JFXDialogLayout
	JFXDialogLayout layout = new JFXDialogLayout();
	layout.setHeading(new Label("Enter Username"));
	layout.setBody(new VBox(new Label("Please enter the username of the person you would like to add."),
			usernameTextField));
	
	// Buttons get added into the actions section of the layout.
	JFXButton addButton = new JFXButton("ADD");
	addButton.setDefaultButton(true);
	addButton.setOnAction(addEvent -> {
		// When the button is clicked, we set the result accordingly
		alert.setResult(usernameTextField.getText());
		alert.hideWithAnimation();
	});
	
	JFXButton cancelButton = new JFXButton("CANCEL");
	cancelButton.setCancelButton(true);
	cancelButton.setOnAction(closeEvent -> alert.hideWithAnimation());
	
	layout.setActions(addButton, cancelButton);
	alert.setContent(layout);

	Optional<String> result = alert.showAndWait();
	if (result.isPresent()){
		System.out.println("Your choice: " + result.get());
	}
});

The sticky part of it is what happens if the result is never set (like in the case of the cancel button): Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: javafx.scene.control.ButtonType cannot be cast to java.lang.String

TurekBot avatar Mar 26 '18 20:03 TurekBot

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jan 07 '19 07:01 stale[bot]

Recently I discovered a way to avoid the ClassCastException that occurs if the user enters/clicks nothing (the one I mentioned in my last comment).

The default result converter returns a result of type ButtonType, even if the result is never set and no buttons are ever pressed, and that causes the ClassCastException. If we override it like this

    alert.setResultConverter(buttonType -> {
                // We don't want to use a result converter, 
                // if there is no result set explicitly, then return null.
                return null;
            });

then it will no longer try to return a ButtonType when no result is set. The result will be null if the user chooses nothing the first time—but that's what an Optional is for.

With this strategy, if we reuse the dialog, it's important to reset the result after you use it. If you don't, the same result sticks around for next time you use the dialog.

    Optional<String> result = alert.showAndWait();
    if (result.isPresent()){
        System.out.println("Your choice: " + result.get());
        alert.setResult(null); // reset the result for next time
    }

TurekBot avatar May 07 '19 14:05 TurekBot

If I understand correctly, the reason for this is because JFXAlert doesn't use ButtonTypes. Through a series of events native to Dialog, the ButtonData.CANCEL_CLOSE is returned because no result is specified.

I may have found a way to work around this. Say we don't use ButtonTypes, still. But we do set the result in the setOnAction method of the buttons we add to the actions section.

An example:

JFXTextField usernameTextField = new JFXTextField();

addUserButton.setOnAction(event -> {
	// Ensure that the user can't close the alert.
	JFXAlert<String> alert = new JFXAlert<>((Stage) addUserButton.getScene().getWindow());
	alert.initModality(Modality.APPLICATION_MODAL);
	alert.setOverlayClose(false);
	
	// Create the content of the JFXAlert with JFXDialogLayout
	JFXDialogLayout layout = new JFXDialogLayout();
	layout.setHeading(new Label("Enter Username"));
	layout.setBody(new VBox(new Label("Please enter the username of the person you would like to add."),
			usernameTextField));
	
	// Buttons get added into the actions section of the layout.
	JFXButton addButton = new JFXButton("ADD");
	addButton.setDefaultButton(true);
	addButton.setOnAction(addEvent -> {
		// When the button is clicked, we set the result accordingly
		alert.setResult(usernameTextField.getText());
		alert.hideWithAnimation();
	});
	
	JFXButton cancelButton = new JFXButton("CANCEL");
	cancelButton.setCancelButton(true);
	cancelButton.setOnAction(closeEvent -> alert.hideWithAnimation());
	
	layout.setActions(addButton, cancelButton);
	alert.setContent(layout);

	Optional<String> result = alert.showAndWait();
	if (result.isPresent()){
		System.out.println("Your choice: " + result.get());
	}
});

The sticky part of it is what happens if the result is never set (like in the case of the cancel button): Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: javafx.scene.control.ButtonType cannot be cast to java.lang.String

thanks.it works! I want to put your codes to generate a DialogBuilder which is the same as theAlertDiglogBuilder in Android delevopment

stars-one avatar Jun 03 '19 01:06 stars-one