Is there a way to do a low level bind but also be able to still do setDisable(ture/false) to a controller?
For example:
HBoxSomeBox.disableProperty().bind(new BooleanBinding() {
{
bind(someIntValue);
}
#Override
protected boolean computeValue() {
return someIntValue >=2 ;
}
});
And somewhere else in the code to do HBoxSomeBox.setDisable(false).
Currently when I try to do that it throws an exception:
java.lang.RuntimeException: HBox.disable : A bound value cannot be set.
So is there another way to have a bound controller but also to be able to set it?
From the comments, you appear to want to disable your control anytime the value of someIntValue is at least two, or under other circumstances "dictated by the view". You could either create a BooleanProperty representing those other circumstances, and use it in the binding:
IntegerProperty someIntProperty = ... ;
BooleanProperty forceDisable = new SimpleBooleanProperty();
hboxSomeHBox.disableProperty().bind(new BooleanBinding() {
{
bind(someIntValue, forceDisable);
}
#Override
public boolean computeValue() {
return someIntValue.get() >= 2 || forceDisable.get() ;
}
}
or, more succinctly,
BooleanProperty forceDisable = new SimpleBooleanProperty();
hboxSomeHBox.disableProperty().bind(someIntValue.greaterThanOrEqualTo(2).or(forceDisable));
Then calling forceDisable.set(true); will disable the control.
You can also achieve this simply with a listener:
someIntValue.addListener((obs, oldValue, newValue) -> {
if (newValue.intValue() >= 2) {
hboxSomeHBox.setDisable(true);
}
});
Since the disable property is not bound, you are free to set it in the usual way.
Related
I would like to understand how to create a custom setter in Lombok and apply the setter on specific member. I have a class with 100 members, and for 50 of them I have a custom setter that check something X before I set the value, and another 50 that have a custom setter that check something Y before the I set the value. Can it be done?
this is a exmple ,
2 members 2 diffrent setters ,
this code is repeated for all members in my class :
#JsonProperty("TAC_LAC_Start_UE1")
private Integer tacLacStartUe1;
#JsonProperty("TAC_LAC_Start_UE2")
private Integer tacLacStartUe2;
#Override
public void setTacLacStartUe1(Integer tacLacStartUe1) {
if (Objects.equals(getTacLacStartUe1(), tacLacStartUe1)) {
return;
}
this.tacLacStartUe1 = tacLacStartUe1;
if (DocKind.ORIG == docKind) {
((EventDocument) prepareDirtyDocument()).setTacLacStartUe1(tacLacStartUe1);
}
}
#Override
public Integer getTacLacStartUe2() {
return tacLacStartUe2;
}
#Override
public void setTacLacStartUe2(Integer tacLacStartUe2) {
if (Objects.equals(getTacLacStartUe2(), tacLacStartUe2)) {
return;
}
this.tacLacStartUe2 = tacLacStartUe2;
if (DocKind.ORIG == docKind) {
((EventDocument) prepareDirtyDocument()).setTacLacStartUe2(tacLacStartUe2);
}
}
Based on the current version's documentation (https://projectlombok.org/features/GetterSetter), it doesn't seem to include a way to specify custom checks for the setter (or getter). I fear you will have to manually code each and every setter.
The same applies for the experimental #Accessor feature.
As #Laf said, Lombok doesn't currently support this feature. However, you still can get rid of some duplicated code by extracting setters logic to the following higher-order function:
private void doSetTacLacStartUe(
Integer oldValue,
Integer newValue,
Consumer<Integer> setter,
BiConsumer<EventDocument, Integer> eventDocumentUpdater
) {
if (Objects.equals(oldValue, newValue)) return;
setter.accept(newValue);
if (DocKind.ORIG == docKind)
eventDocumentUpdater.accept((EventDocument) prepareDirtyDocument(), newValue);
}
And using it this way:
public void setTacLacStartUe1(Integer tacLacStartUe1) {
doSetTacLacStartUe(getTacLacStartUe1(), tacLacStartUe1, it -> this.tacLacStartUe1 = it, EventDocument::setTacLacStartUe1);
}
I tried disable button when some integer value != 1
For example (my idOrder is IntegerProperty)
refreshButton.disableProperty().bind(new BooleanBinding() {
#Override
protected boolean computeValue() {
return currentOrder.getIdOrder() != 1;
}
});
And it's works. But when I changed value on 1 (currentOrder.setIdOrder(1)) button is still disabled.
What I doing wrong?
You've created a BooleanBinding but haven't configured it to observe anything, thus it will never be notified of your property changing. You need to invoke BooleanBinding#bind(Observable...) during instantiation. For example:
refreshButton.disableProperty().bind(new BooleanBinding() {
{
bind(currentOrder.idOrderProperty());
}
#Override protected boolean computeValue() {
return currentOrder.getIdOrder() != 1;
}
#Override public void dispose() {
// for a proper implementation, we need this as well
unbind(currentOrder.idOrderProperty());
}
});
That said, the above can be simplified with Bindings#createBooleanBinding(Callable,Observable...):
refreshButton.disableProperty()
.bind(Bindings.createBooleanBinding(
() -> currentOrder.getIdOrder() != 1, currentOrder.idOrderProperty()));
But even that can be simplified further with one of the following:
Bindings#notEqual(int,ObservableNumberValue):
refreshButton.disableProperty().bind(Bindings.notEqual(1, currentOrder.idOrderProperty());
NumberExpresion#isNotEqualTo(int):
refreshButton.disableProperty().bind(currentOrder.idOrderProperty().isNotEqualTo(1));
I would like to understand how to create a custom setter in Lombok and apply the setter on specific member. I have a class with 100 members, and for 50 of them I have a custom setter that check something X before I set the value, and another 50 that have a custom setter that check something Y before the I set the value. Can it be done?
this is a exmple ,
2 members 2 diffrent setters ,
this code is repeated for all members in my class :
#JsonProperty("TAC_LAC_Start_UE1")
private Integer tacLacStartUe1;
#JsonProperty("TAC_LAC_Start_UE2")
private Integer tacLacStartUe2;
#Override
public void setTacLacStartUe1(Integer tacLacStartUe1) {
if (Objects.equals(getTacLacStartUe1(), tacLacStartUe1)) {
return;
}
this.tacLacStartUe1 = tacLacStartUe1;
if (DocKind.ORIG == docKind) {
((EventDocument) prepareDirtyDocument()).setTacLacStartUe1(tacLacStartUe1);
}
}
#Override
public Integer getTacLacStartUe2() {
return tacLacStartUe2;
}
#Override
public void setTacLacStartUe2(Integer tacLacStartUe2) {
if (Objects.equals(getTacLacStartUe2(), tacLacStartUe2)) {
return;
}
this.tacLacStartUe2 = tacLacStartUe2;
if (DocKind.ORIG == docKind) {
((EventDocument) prepareDirtyDocument()).setTacLacStartUe2(tacLacStartUe2);
}
}
Based on the current version's documentation (https://projectlombok.org/features/GetterSetter), it doesn't seem to include a way to specify custom checks for the setter (or getter). I fear you will have to manually code each and every setter.
The same applies for the experimental #Accessor feature.
As #Laf said, Lombok doesn't currently support this feature. However, you still can get rid of some duplicated code by extracting setters logic to the following higher-order function:
private void doSetTacLacStartUe(
Integer oldValue,
Integer newValue,
Consumer<Integer> setter,
BiConsumer<EventDocument, Integer> eventDocumentUpdater
) {
if (Objects.equals(oldValue, newValue)) return;
setter.accept(newValue);
if (DocKind.ORIG == docKind)
eventDocumentUpdater.accept((EventDocument) prepareDirtyDocument(), newValue);
}
And using it this way:
public void setTacLacStartUe1(Integer tacLacStartUe1) {
doSetTacLacStartUe(getTacLacStartUe1(), tacLacStartUe1, it -> this.tacLacStartUe1 = it, EventDocument::setTacLacStartUe1);
}
So, the way I have things set up, is that at specific conditions, I want a listener to be active, and after it has fired, stop listening for changes. Maybe I'm missing something, but I can't seem to figure out how to use the removeListener() function, or if that's even the way to go about doing this.
Some code:
break1.setOnAction(e ->{
final String fieldValue = manage_money.getText();
int pp = Integer.parseInt(platinum_num.getText());
int gp = Integer.parseInt(gold_num.getText());
int sp = Integer.parseInt(silver_num.getText());
int cp = Integer.parseInt(copper_num.getText());
if (fieldValue != null && fieldValue.matches("\\d+")) {
int value = Integer.parseInt(manage_money.getText());
if (silver.isSelected()){
if (value <= sp){
try {
tooltip_inv.getChildren().addAll(select);
radio_money2.selectedToggleProperty().addListener(((observable, oldValue, newValue) -> {
if (newValue == copper){
silver_num.setText(Integer.toString(sp - value));
copper_num.setText(Integer.toString(cp + value * 10));
manage_money.clear();
tooltip_inv.getChildren().clear();
}
}));
} catch (IllegalArgumentException ex) {
}
}else{
manage_money.setText("ERR");
}
}
}else{
manage_money.setText("NaN");
}
});
What you've done in your example is you've defined a listener in your addListener method. You don't have any reference to it except there! One way to fix this is to simply create a variable for it and keep it somewhere, something like
ChangeListener listener = new ChangeListener(){
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
// your code here
}
};
radio_money2.selectedToggleProperty().addListener(listener);
radio_money2.selectedToggleProperty().removeListener(listener);
I have several TextFields and a submit button. I want my button to be disabled unless all the textfields have been validated. Below is my code for the BooleanBindings:
BooleanBinding firstNameValidation, middleNameValidation, lastNameValidation,
usernameValidation, passwordValidation, retypePasswordValidation, emailValidation, phoneNumberValidation;
firstNameValidation = Bindings.createBooleanBinding(()->{
if(employee.setFirstName(txtFirstName.getText()) == 0){
piFirstName.setProgress(100);
return true;
} else {
piFirstName.setProgress(0);
return false;
}
}, txtFirstName.textProperty());
middleNameValidation = Bindings.createBooleanBinding(()->{
if(employee.setMiddleName(txtMiddleName.getText()) == 0){
piMiddleName.setProgress(100);
return true;
} else {
piMiddleName.setProgress(0);
return false;
}
}, txtMiddleName.textProperty());
..and so on.
This is how I try to bind the disableProperty of the submit button to the BooleanBindings:
btnSetPermissions.disableProperty().bind(firstNameValidation.not().or(middleNameValidation.not()).or(lastNameValidation.not())
.or(usernameValidation.not()).or(passwordValidation.not()).or(retypePasswordValidation.not())
.or(emailValidation.not()).or(phoneNumberValidation.not()));
It works, but if the first condition returns false, it doesn't check the rest of the conditions because of the OR-operation.
I can't seem to find a solution. Any help would be much appreciated.
You're mixing your functionality too much. Separate it out into constituent components:
firstNameValidation = Bindings.createBooleanBinding(() -> {
String firstName = txtFirstName.getText();
if (/* firstName is valid */) {
return true ;
} else {
return false ;
}
}, txtFirstName.textProperty());
employee.firstNameProperty().bind(txtFirstName.textProperty());
// if you want employee.firstName to change only if the name is valid,
// use a listener here instead of the binding above:
txtFirstName.textProperty().addListener((obs, oldText, newText) -> {
if (firstNameValidation.get()) {
employee.setFirstName(newText);
}
});
piFirstName.bind(Bindings.when(firstNameValidation).then(100).otherwise(0));
// similarly for other properties...
// then (this logic is equivalent to yours, but easier to read imho)
btnSetPermissions.disableProperty().bind(
(firstNameValidation.and(middleNameValidation)
.and(userNameValidation).and(passwordValidation)
.and(retypePasswordValidation).and(emailValidation)
.and(phoneNumberValidation)
).not());
Note you can, of course, reduce the code by moving anything repetitive to a method in the usual way:
private BooleanBinding createValidationBinding(
TextField field, Predicate<String> validationRule,
StringProperty boundValue, ProgressIndicator indicator) {
BooleanBinding validation = Bindings.createBooleanBinding(() -> {
String value = field.getText();
return validationRule.test(value);
}, field.textProperty());
field.textProperty().addListener((obs, oldText, newText) -> {
if (binding.get()) {
boundValue.set(newText);
}
});
indicator.progressProperty().bind(Bindings.when(validation).then(100).otherwise(0));
return validation ;
}
Then you can do
firstNameValidation = createValidationBinding(txtFirstName,
name -> /* boolean indicating if name is valid */,
employee.firstNameProperty(), piFirstName);
and similarly for the other fields.
Personally, this is what I do:
btnSetPermissions.disableProperty.bind(Bindings.createBooleanBinding(() -> {
if (!validateFirstName() ||
!validateMiddleName() /* and so on */)
return true;
else return false;
}, firstNameProperty(), middleNameProperty() /* and other properties*/ ));
public boolean validateFirstName() {
// Your validation logic
}
public boolean validateMiddleName() {
// Your validation logic
}
/* Other validation methods */
There is no need to create so many objects (those BindingExpression objects), and you can easily use conditional operators like || and && which will guarantee skipping of non-essential checks whenever possible.