Reputation: 23
I have a TextField and two buttons in a row. One button (the 'add button') adds another row of a TextField and another pair of add and delete button, the other button deletes the row. The delete button is disabled while the current row is the only row, so there can't be no rows.
The add button is enabled only if the textfield of the current row is not empty and if it is the last textfield. So every row has a disabled 'add button' except for the last one.
My question now is how can I bind the 'add button' disableProperty to all textfields that exist and check if they are empty. As a matter of fact, I think I only have to check the last textfield and if it is empty I disable the last 'add button' if something is written the last 'add button' remains disabled but the current one of the row gets enabled.
I have found a workaround, in which I bind the button to the textfield, then if I add another row I unbind the button, disable it, and if I delete a row I only enable the last button and bind it againt to the textfield. This solution seems very clunky and I was wondering if there is a more elgant solution with property binding.
My code (with the workaround, so you can see what I want to do):
public class Controller {
@FXML
private VBox VBox;
public ObservableList<TextField> oList = FXCollections.observableArrayList();
public ObservableList<Button> bList = FXCollections.observableArrayList();
public void initialize(){
createRow();
}
private void createRow(){
HBox box = new HBox(10);
TextField textField = new TextField();
Button addButton = new Button("Add row");
Button deleteButton = new Button("Delete");
box.getChildren().addAll(textField, addButton, deleteButton);
VBox.getChildren().add(box);
oList.add(textField);
bList.add(addButton);
addButton.disableProperty().bind(Bindings.isEmpty(textField.textProperty()));
addButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
addButton.disableProperty().unbind();
createRow();
textField.setDisable(true);
addButton.setDisable(true);
}
});
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
int idx = oList.indexOf(textField);
oList.remove(idx);
bList.remove(idx);
VBox.getChildren().remove(idx);
for(TextField tf : oList){
int i = oList.indexOf(tf);
if(oList.size()-1 == i){
tf.setDisable(false);
bList.get(i).disableProperty().bind(Bindings.isEmpty(tf.textProperty()));
}
}
}
});
}
}
And two Screenshots:
4 rows added, all 'add buttons' disabled, also the last one bc nothing is written in textfield
Here I deleted row test1 and the empty row and all 'add buttons' are still disabled except for the last one because there is text in the text field
Thanks for your help!
PS: I know in my Code ObservableLists aren't necessary, but I was trying things out and let them in because I forgot...
Upvotes: 1
Views: 420
Reputation: 4258
You can create a BooleanExpression
that returns true if all TextField
s in the list are empty. Note that you need to recreate that expression as the contents of the ObservableList<TextField>
' change:
oList.addListener((ListChangeListener<? super TextField>) c -> {
addButton.disableProperty().unbind();
BooleanExpression allEmpty = oList.stream()
.map(tf -> BooleanExpression.booleanExpression(tf.textProperty().isEmpty()))
.reduce(new SimpleBooleanProperty(true), BooleanExpression::and);
addButton.disableProperty().bind(allEmpty);
});
Everytime a new TextField
is added or removed, each TextField
in the list will be mapped to a BooleanExpression
of the empty
property of that TextField
. Then, all expressions will be anded together.
Note: The same way can also be done using a loop instead of a stream.
Note: You need to add this listener before adding any element to the list.
Upvotes: 1