Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where should I validate JavaFX Property changes?

I have a mvp structured javafx application. There is a view with a textfield, which has its own textProperty of type StringProperty. There is also a model which contains an Object called Item. Item has an IntegerProperty.

Now I'd like to bind these two Properties within my presenter-class, so that they get updated, when one or another changes. Eventhough they have different types, there is the possibility to bind them the following way:

Bindings.bindBidirectional( textField.textProperty(), item.percentProperty(), new NumberStringConverter() );

This works perfectly fine, unless the value of the textfield gets cleared, which results in a NullPointerException, because an empty value of textProperty results in a Null-Value and setting a null Value in IntegerProperty results in a NullPointerException. Can you think of any way to avoid this? Do I have to write my own NumberStringConverter?

Moreover I'd like to define, that Item can only hold a percent value between 0 and 100. The View should be informed, when the value is invalid, so the user can get feedback. Where should I verify these kind of businessrules?

I came up with a first example, but I am not sure, if that should be the way to go, so I'd be curious, if you might have better ideas how to solve this.

class PercentProperty extends SimpleIntegerProperty
{
  private InvalidValueListener invalidValueListener = null;

  public PercentProperty ( final Integer defaultValue )
  {
    set( defaultValue );
  }

  @Override
  public void set( final int newValue )
  {
    if ( isValid( newValue ) )
    {
      super.set( newValue );
      if ( invalidValueListener != null )
        invalidValueListener.validValue();
    }
    else
    {
      if ( invalidValueListener != null )
        invalidValueListener.invalidValue();
    }
  }

  private boolean isValid( final int value )
  {
    return (value >= 0 && value <= 100);//FIXME: Better use Predicates to define Rules.
  }

  public void setListener( final InvalidValueListener listener )
  {
    invalidValueListener = listener;
  }

  public void removeListener( @SuppressWarnings( "unused" ) final InvalidValueListener listener )
  {
    invalidValueListener = null;
  }

  protected void fireInvalidationValue()
  {
    invalidValueListener.invalidValue();
  }
}


interface InvalidValueListener
{
  void validValue();
  void invalidValue();
}
like image 694
crusam Avatar asked Apr 11 '14 13:04

crusam


2 Answers

JavaFX is a simple graphical toolkit, not a comprehensive framework, and this means that lots of things you have to engineer yourself. Data validation is such a thing, and you have to find your own way among your previous experience and others' suggestions.

I would not bind the two properties: the text field should be initialized (just set, not bound, to avoid glitches while the user is typing without her explicit consensus) with the value from the model, and then the integer property should be updated by a listener (a text field's ChangeListener or a listener to the form submission, if appliable and depending on your likes), which is responsible for validating input and reporting errors to the user.

This way you decouple two things that are indeed unrelated: one is a widget for accepting user input (a text you need to parse to get a number), and the other is a number in your model, which is used to make a computation.

As a side note, I would not use two properties altogether, and I'd revisit your three tiers parition. MVP and all MVC derivatives proved to be good patterns to build GUI toolkits, but I was never convinced they were equally good for structuring GUI applications. I mean, if what you call model is a way to share session data between different parts of the application (kind of an events sink) then it's a perfectly legitimate implementation, otherwise I see no use in having a separate bunch of properties grouped in a class. In the latter case, the widgets themselves are the model:

// This is the controller
public class PesonalDetails {
  // Model starts here: it's implicitely defined by the widgets
  // You may also use @FXML
  private final TextField first = new TextField();
  private final TextField last = new TextField();
  // Model ends here
} 

Note I'm not saying MVC should be thrown away and everything should be collapsed in one single file. Just that MVC, MVP, MVVM are design patterns and it's up to you to decide when, where and how to implement them - depending on how much they buy to you. With JavaFX I like to use these tiers:

  • A visual layout tier (a layout builder implemented in Java or FXML)
  • Event handling code
  • If appliable, a data access layer (and you can apply a pattern here, like ActiveRecord)
like image 190
Raffaele Avatar answered Sep 19 '22 12:09

Raffaele


(The new version of the answer)

I think the best aproach is to not let a user enter an incorrect value in the first place. You can achive this easily with help of JideFX Fields:

FormattedTextField<Integer> field = new FormattedTextField<>();
field.getPatternVerifiers().put("p", new IntegerRangePatternVerifier(0, 100));
field.setPattern("p");
field.valueProperty().bindBidirectional(item.percentProperty());

Particularly FormattedTextField is very convenient because it do text-to-value conversion and validation for you, so there is no need to implement any utility classes yourself.

Links:

JideFX Fields Developer Guide: http://www.jidesoft.com/jidefx/JideFX_Fields_Developer_Guide.pdf

Source code: https://github.com/jidesoft/jidefx-oss

Binary: http://search.maven.org/#search%7Cga%7C1%7Cjidefx

like image 40
dened Avatar answered Sep 21 '22 12:09

dened