How to get entered value in editable ComboBox in JavaFx?

15,118

Solution 1

By default, most JavaFX controls don't "commit" a value on losing focus - so TextFields, when used as editing controls inside a larger control such as a ComboBox don't commit the value back to the parent control unless the user presses Enter. There is quite a lot of controversy about this design decision: however it is the decision that was made and if we are to use JavaFX we have to either accept that or find ways to work around it to get the behavior we want.

One "quick and dirty" approach would be to dig into the text field and get the text from it, as in @Ajeetkumar's answer:

selected.setText(emailComboBox.getEditor().getText());

The problem with this approach is that it leaves the application in an inconsistent state: the value displayed to the user in the label is not the value held in the model of the combo box: in other words emailComboBox.getValue() returns a different value to the one displayed in the label (since the value from the text field was never committed to the combo box). At some point your application will need to process the data displayed, and the natural place the programmer (or another member of the team) will look for the data is in the combo box's model: i.e. they will expect the data to be in emailComboBox.getValue(). A better approach is to make sure the combo box's value updates when you want it to, and then just use the expected emailComboBox.getValue() to get the data.

For example, you could update the value when the text field loses focus:

    emailComboBox.getEditor().focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
        if (! isNowFocused) {
            emailComboBox.setValue(emailComboBox.getEditor().getText());
        }
    });

and then the natural:

    b.setOnAction(e -> {
        selected.setText(emailComboBox.getValue());
    });

With this approach, the application is always in a consistent state: i.e. the label is displaying the value of the combo box, which is far less likely to cause bugs later on than setting the label directly from the text field.

Another variation is to update the combo box value as soon as the text in the editor changes:

    emailComboBox.getEditor().textProperty().addListener((obs, oldText, newText) -> {
        emailComboBox.setValue(newText);
    });

Note that, if you wanted, with this version you could actually dispense with the button entirely and just bind the label's text directly to the combo box's value:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class Comparator extends Application {

    @Override
      public void start(Stage stage) {
        stage.setTitle("ComboBoxSample");
        Scene scene = new Scene(new Group(), 450, 250);

        ComboBox<String> emailComboBox = new ComboBox<>();
        emailComboBox.getItems().addAll("A","B","C","D","E");

        emailComboBox.setEditable(true);        

        emailComboBox.getEditor().textProperty().addListener((obs, oldText, newText) -> {
            emailComboBox.setValue(newText);
        });


        GridPane grid = new GridPane();
        grid.setVgap(4);
        grid.setHgap(10);
        grid.setPadding(new Insets(5, 5, 5, 5));
        Label to = new Label("To: ");
        Label selected = new Label();
        grid.add(to, 0, 0);
        grid.add(emailComboBox, 1, 0);
        grid.add(selected, 2, 0);

        selected.textProperty().bind(emailComboBox.valueProperty());

        Group root = (Group) scene.getRoot();
        root.getChildren().add(grid);
        stage.setScene(scene);
        stage.show();
      }

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

Keeping the integrity of the data is highly recommended for keeping the application consistent and making it less likely to introduce bugs later on, so it's highly recommended to update the combo box's value property to contain the value you want to represent, and then to refer to that value. The text field used as an editor is really an implementation detail of the combo box that should only be used to modify the behavior of the combo box editing process: in this example it's used to modify the update behavior - you could also set a formatter on it, etc. It shouldn't be used as a proxy for the model, as this will make life difficult later.

Solution 2

I got the answer.

emailComboBox.getEditor().getText()

The textField where we type in an editable ComboBox is known as the editor of the ComboBox. And it's a normal TextField object. To access that object, you need to use the method ComboBox.getEditor(). This way you can use the methods of the TextField class.

Share:
15,118
Ajeetkumar
Author by

Ajeetkumar

Updated on June 08, 2022

Comments

  • Ajeetkumar
    Ajeetkumar almost 2 years

    I have a ComboBox which lists the values set. I have made it editable using setEditable(true) method but how to get the value entered by user? I tried getSelectionModel().getSelectedItem() and getValue() method but didn't succeed.

    Here is the code.

    public class Comparator extends Application {
    
        @Override
          public void start(Stage stage) {
            stage.setTitle("ComboBoxSample");
            Scene scene = new Scene(new Group(), 450, 250);
    
            ComboBox<String> emailComboBox = new ComboBox<>();
            emailComboBox.getItems().addAll("A","B","C","D","E");
    
            emailComboBox.setEditable(true);        
    
            Button b = new Button("get text");
    
            GridPane grid = new GridPane();
            grid.setVgap(4);
            grid.setHgap(10);
            grid.setPadding(new Insets(5, 5, 5, 5));
            Label to = new Label("To: ");
            Label selected = new Label();
            grid.add(to, 0, 0);
            grid.add(emailComboBox, 1, 0);
            grid.add(b, 2, 0);
            grid.add(selected, 3, 0);
    
            b.setOnAction(e -> {
                selected.setText(emailComboBox.????);
            });
    
            Group root = (Group) scene.getRoot();
            root.getChildren().add(grid);
            stage.setScene(scene);
            stage.show();
          }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    I actually want to get the newly entered value. PS: Just to avoid the confusion if in case. I can enter any String which doesn't exist in the list already. this will be a new value entered.