Reputation: 448
If I have more than one TextField
in the context of a form, how can a 'submit' button remain disabled until a arbitrary number of TextFields
are not blank?
This can be done simply if only one TextField
is present with a ChangeListener
on the text field but how could it be implemented for x TextField
's?
Where x is a number > 1.
One TextField Example
The .setDisable()
method of the button is implemented with a ChangeListener
on the TextField
checking if the field is empty.
This behaves:
... as expected.
If the same Listener is utilised with both fields (as below), they behave as if they were independent of each other.
eg. If either one contains something, then the button is enabled:
public void someMethod(){
Dialog dialog = new Dialog();
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.CANCEL);
TextField textField1 = new TextField();
textField1.setPromptText("field1");
TextField textField2 = new TextField();
textField2.setPromptText("field2");
// add fields to gridpane
GridPane gridPane = new GridPane();
gridPane.add(textField1,0,0);
gridPane.add(textField2,0,1);
// add grid to dialog pane
dialog.getDialogPane().setContent(gridPane);
Node applyButton = dialog.getDialogPane().lookupButton(ButtonType.APPLY);
applyButton.setDisable(true);
ChangeListener<String> myChangeListener = new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
applyButton.setDisable(newValue.trim().isEmpty());
}
};
textField1.textProperty().addListener(myChangeListener);
textField2.textProperty().addListener(myChangeListener);
dialog.showAndWait();
}
This is also the case, if independent Listeners are added to each field using the same process:
textField1.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
applyButton.setDisable(newValue.trim().isEmpty());
}
});
textField2.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
applyButton.setDisable(newValue.trim().isEmpty());
}
});
Multiple Attempt
One solution for multiple is to simply 'layer' the listeners within in each other. This behaves as intended, with both TextFields having to contain something before the button is enabled.
textField1.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
// add listener to textField 2
textField2.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
applyButton.setDisable(newValue.trim().isEmpty());
// ... add listener to textField x
}
});
}
});
This process seems very inefficient!
What if I have 20 TextFields
? Should I be 'layering' 20 deep?
Is this the only way to do this? If not, how can the process be more efficient?
Upvotes: 1
Views: 71
Reputation: 6921
You don't need to add extra listeners inside other listeners - you want to observe the change regardless of whether a previous change happened or not.
Instead, you must check for the validity of the entire input (all text-fields are non-empty) with each change. This can be done fairly generically by passing all TextField
s to one method:
private void setButtonTextFieldListeners(Button button, TextField... textFields) {
ChangeListener<String> listener = (observable, oldValue, newValue) -> {
for (int i=0; i<textFields.length; i++) {
if (textFields[i].getText()==null || textFields[i].getText().trim().isEmpty()) {
button.setDisable(true);
return;
}
}
button.setDisable(false);
}
for (TextField textField : textFields) {
textField.textProperty().addListener(listener);
}
}
Alternatively, you can build a BooleanExpression
by and
ing all of your conditions, then binding the button's disable
property to its negation:
BooleanExpression b = textField1.textProperty().isNotEmpty()
.and(textField2.textProperty().isNotEmpty())
.and(...);
// b is True if and only if ALL text fields are not-empty. The button should be disabled when this ISN'T the case (enabled when b is True).
button.disableProperty().bind(b.not());
Upvotes: 1