JavaFX Forms Framework Part 3


Introduction

Matthew 7:13-14

Matthew 7:13-14

This is the third installment of a series of blog entries relating to a proof of concept for a JavaFX Forms Framework. If you missed the beginning of the series you may go to Part 1 and Part 2. We will take a look at code snippets relating to how the FXForms Framework was implemented. If you want to jump right into the code you may download it here or browse the source code here from the JFXtras Samples area. To those who are following the series will notice similarities to JGoodies Bindings and Validation libraries, it is because of those libraries and presentations which basically inspired me to create this MVC forms framework in JavaFX.

Disclaimer: Most of the code snippets will deal with the user of the API’s perspective as opposed to the implementer of the API’s perspective in order to keep the blog entry short. I will try my best to touch on areas regarding the framework’s underlying implementation. I advise people to check the code project out, review it and run it.

A thing I’d like to bring to your attention is that I decided to refactor the code a little and add a new feature to the FXForms Framework. I refactored the form to reference an instance of a presentation model instead of inheriting from it. As I mentioned in Part 2 the form will be independent of the presentation model. This provides different validation contexts while reusing the same form. An example of this situation is when a user uses a form to ‘Add’ information versus an ‘Edit’ of the form information. The new feature added is the ability to validate a field as the user is typing into the text box and positions an icon to indicate an error, warning or information to the user. Another feature might be to add tool tips when the mouse hovers over the icon similar to JGoodies’ IconFeedbackPanel behavior in Java Swing. Before going further into the implementation details you may want to launch the demo to get a feel for the behavior of the entry form with validation and icon indicators.

Demo

Demo

Instructions:

  • Enter numbers or symbols into the first, last and middle name field.
  • Click on the check box to swap the JavaBean for the form.
  • Observe the underlying bean values changing.

Next are the steps on how to develop the demo using the FXForms Framework.

Developer Steps

  1. Create a JavaBean representing a domain object.
  2. Create a Presentation Model with validation for a Form.
  3. Create a Form
  4. Associate a Presentation Model to Form
  5. Use the Form in an application

Detailed Steps

Step 1. Create a JavaBean representing a domain object.

// Java
public class PersonBean extends DomainModel{
    public static final String FIRST_NAME_PROPERTY = "firstName";
    public static final String MIDDLE_NAME_PROPERTY = "middleName";
... // more strings naming properties
    private String firstName;
    private String middleName;
... // more attributes

    /**
     * Returns first name of the person.
     * @return
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * Sets the first name of the person.
     * @param firstName
     */
    public void setFirstName(String firstName) {
        String old = this.firstName;
        this.firstName = firstName;
        firePropertyChange(FIRST_NAME_PROPERTY, old, firstName);
    }
... // the rest of the methods.
}

PersonBean.java – A domain object containing property change support.

DomainModel.java – Abstract base class containing property change support.

Step 2. Create a Presentation Model with validation for a Form

As I mentioned earlier about reusing the same form with different presentation models. Below you will see an ‘Add Form’ with validation on the Last Name field. The error icon indicates that the last name may not contain symbols and numbers, but allowing letters, apostrophe or hyphen in the name. You will notice the red error icon beside the ‘last name’ text field.

Add Form validation on last name field.

Add Form validation on last name field.

    // JavaFX
    var personForm:NameForm = NameForm{
        presentationModel:domain.model.personpresentationmodel.AddPersonPM{}
    };
    personForm.presentationModel.jBean = new PersonBean();

Next, you will see an ‘Edit Form’ with no validation on the ‘Last Name’ field. But, there is validation on the ‘First Name’ field. The warning icon indicates that the first name can contain symbols and numbers, letters, apostrophe or hyphen, but isn’t recommended. You will notice the yellow warning icon beside the ‘first name‘ field.

Edit Form no validation for Last Name field

Edit Form no validation for Last Name field

    // JavaFX
    personForm.presentationModel = domain.model.personpresentationmodel.EditPersonPM{}
    personForm.presentationModel.jBean = personBean2;

*Note: The examples above are two hypothetical use cases, I mocked up those forms to help illustrate different validation contexts. The demo app uses an edit presentation model that does validate on the ‘last name‘ field.

Edit Person Presentation Model w/Validation

// JavaFX
public class EditPersonPM extends fxforms.model.model.PresentationModel {

   /** Validate the first name field */
   var validateFirstName =  Validator{
       id:PersonBean.FIRST_NAME_PROPERTY
       public override function validate(value:Object){
           return validateName(value, PersonBean.FIRST_NAME_PROPERTY, "Warning");
       }
   };
... // more validators
   postinit {
       addValidator(validateLastName);
       addValidator(validateFirstName);
       addValidator(validateMiddleName);
   }
}
// Script level function
/**
 * Using regular expression allow letters, apostrophe, hyphen
 */
function validateName(value:Object, propName:String, messageType:String){ // use friendly names, short names, etc.
    var results = ValidationResult{};
    var strValue:String = value as String;
    var found:Boolean = Pattern.matches("^[a-zA-Z,'.\\-\\s]*$", strValue);
    if (not found) {
        var message:FieldMessage = FieldMessage{
            id:propName
            messageType:messageType
            errorId:"123"
            errorMessage:"No symbols in names except - or ' (apostrophe)"
        }
        results.addMessage(message);
    }
    return results;
}

Line 01: Class EditPersonPM extends fxforms.model.model.PresentationModel
Line 04: var validateFirstName is an instance of a Validator
Line 12: Adds all Validators to the presentation model
Line 21: Script level function to be used in each validator
Line 24: Regular expression to allow letters, apostrophe and hyphen characters only.
Line 26: Creation of the message when Validator validates.

personpresentationmodel.fx – The edit presentation model for a person name form.

model.fx – Contains presentation model and value model implementation.

validation.fx – Contains the validator, message, result classes.

Step 3. Create a Form

Edit Person Form (Screen mockup)

0 full name panel
+-1------------------------+ // VBox with 3 things
! +-2--------------------+ ! // HBox with 2 things
! ! [ 3 ] [ 4           ]! ! // Label(section) and Label(title)
! +----------------------+ !
! +-5--------------------+ ! // HBox with 2 things
! ! [ 6 ] [ 7  ]         ! ! // Label(spacer} and Text(instructions)
! !                      ! ! // wrapping text abilities
! +----------------------+ !
! +-8--------------------+ ! // HBox with 2 things
! ! +-9---+ +-10-------+ ! ! // VBox_9(labels) Vbox_10(textbox)
! ! ![11] ! ! [15]     ! ! ! // Label(lastname) TextBox()
! ! ![12] ! ! [16]     ! ! ! // Label(firstName) TextBox()
! ! ![13] ! ! [17]     ! ! ! // Label(mi)  TextBox()
! ! ![14] ! ! [18]     ! ! ! // Label(suffix)  TextBox()
! ! +-----+ +----------+ ! !
! +----------------------+ !
+--------------------------+

NameForm inherits from Form
Form inherits from CustomNode

// JavaFX
public class NameForm extends fxforms.ui.form.Form {
   public override function create():Node{
        // 0 main panel
        var mainPanel:Panel = Panel{
            content:[]
        }
        ... // more layouts and widgets

        var firstLabel:Label = Label {
            text: "First Name"
            hpos:HPos.RIGHT
            font : Font {
                size: 18
            }
            layoutInfo: LayoutInfo { minWidth: 100 width: 150 maxWidth: 200 }
        };
        ... // more code
        var lastNameTextBox:TextBox = fxforms.ui.controls.MyTextBox {
            id:"lastName"
            columns:20
        };
        var miNameTextBox:TextBox = fxforms.ui.controls.MyTextBox {
            id:"middleName"
            columns:20
        };
        ... // more fields

        // *** NOTE: This is for easy lookup. And relating to Scene.lookup(id) bug in 1.2.
        guiFields = [lastNameTextBox, firstNameTextBox, miNameTextBox, suffixNameTextBox];
        presentationModel.addGuiFields(guiFields);
        return mainPanel;
    } // create()
} // NameForm

NameForm.fx – This represents a Form containing a person’s name information.

form.fx – This is the base class which contains the presentation model for forms binding behavior.

controls.fx – This contains all registered GUI controls for observing value model value changes. Currently only one control exists the TextBox control.

*Note: The ideal way to build forms is using the JFXtras MigLayout library. To learn more take a look at Dean Iverson’s blog entry called “MigLayout for JavaFX Reloaded“.

Step 4. Associate a Presentation Model to Form

// JavaFX
var personForm:NameForm = NameForm{
    presentationModel:domain.model.personpresentationmodel.EditPersonPM{}
    translateX: bind slideFormX
};
var personBean2:domain.model.PersonBean = new domain.model.PersonBean();
personBean2.setFirstName("Squidward");
personBean2.setLastName("Tentacles");
personBean2.setMiddleName("Nickelodeon");
personBean2.setSuffixName("Sr.");

// set presentation model with domain object
personForm.presentationModel.jBean = personBean2;

Line 02: Associate presentation model to form
Line 05: Create an instance of a JavaBean
Line 12: Bind bean to presentation model and form

Once the presentation model and form are assembled binding an existing Java object is a snap. In part 4 on enhancing this process would be to create a factory to obtain meta information of the form to retrieve nested properties within a POJO/JavaBean off of the JavaFX main thread (desktop profile is the EDT). This effort will help alleviate from the dreaded Hibernate lazy init exception when using detached objects. So, making sure you don’t block the GUI thread is a big deal when it comes to user experience.

Step 5. Use the Form in an application

    var switchPersonButton:CheckBox = CheckBox {
            text: bind personToSwitchText
            width: 100
            translateX: 5
            translateY: bind mainScene.height - switchPersonButton.height - 5
            allowTriState: false
            selected: false
            override var onMouseReleased = function(e:MouseEvent):Void {
                if (selected){
                   personToSwitchText = "Sponge Bob";
                   personForm.presentationModel.jBean = personBean1;
                } else {
                   personToSwitchText = "Squidward";
                   personForm.presentationModel.jBean = personBean2;
                }
            }
        };

    var mainScene:Scene = Scene {
        fill: LinearGradient {
                    startX: 0
                    startY: 0
                    endX: 0
                    endY: 1
                    stops: [
                        Stop { offset: 0.1 color: Color.ORANGE },
                        Stop { offset: 1.0 color: Color.YELLOW },
                    ]
                }
        content: [personForm, switchPersonButton, backButton, nextButton]
    };

Main.fx – The main application file to launch the application

Value Model

The ‘Value Model‘ is probably the most important aspect of how the Forms binding works. The value model is a model that holds a single value that notifies registered listeners that a value has changed. Registered listeners will likely be GUI controls and JavaBean properties. Bidirectional binding occurs when a bean property value changes, which notifies the value model which updates the  GUI control value. This holds true when going in the other direction too, such as the user changes the value of the GUI control which notifies the value model which updates the JavaBean property value.

model.fx – Contains presentation model and value model implementations.

Conclusion

I feel the implementation of the fxforms framework using JavaFX was extremely easy and is a lot less code compared to a Java equivalent of a Swing/SWT forms framework, also carrying additional overhead using 3rd party libraries for binding and validation. As JavaFX matures with more controls the forms framework would need to be flexible enough to add any controls to handle custom bindings such as list models.  Next we will look at Part 4 Enhancements . As always any feedback is welcome!

References

Validation presentation by Karsten Lentzsch – http://www.jgoodies.com/articles/validation.pdf

JGoodies: http://www.jgoodies.com/

JGoodies Support: http://www.jgoodies.com/products/purchase.html

The Unknown JavaBean by Richard Bair – http://weblogs.java.net/blog/rbair/archive/2006/05/the_unknown_jav.html

JavaFX – JMS Unexpected Null Pointer Exception http://blogs.sun.com/clarkeman/entry/javafx_jms_unexpected_null_pointer

JFXtras Community Site – http://jfxtras.org/portal/home

10 thoughts on “JavaFX Forms Framework Part 3

  1. Pingback: JavaFX Forms Framework Part 1 « Carl’s FX Blog

  2. Pingback: JavaFX MVC Forms based framework « JFXStudio: sketch, hack, share

  3. Frank

    Hi,

    I am wondering why you would want to write the validation logic in JavaFX on the front end. What about
    – having a validation class/engine on the server side that you call from the frontend
    – or dynamically loading certain validation code (written in java) on demand into the frontend

    Coding the validation on the front end requires you to double code validation both on the front end and the server side. In my opinion, validation logic should reside in one place only.

    Best
    Frank

  4. carldea Post author

    Frank,

    Good point. I believe you should always have server side validation regardless whenever you are building enterprise applications. On the client side it is a nice thing to have (user experience), which is similar to client side JavaScript on a Web browser. I too believe that validation logic should reside in one place too. To make this enterprise worthy one could centralize validation on the server and push (pull in most cases -ajax) down the validators dynamically into the JavaFX client app in cases where validations would change often. In part 2, I discuss that this was purely client-side proof of concept. Ideally, on the server side when dealing with JPA or Hibernate you have entity level meta information to assist in deriving some validation rules such as mandatory fields, field lengths, etc. There are basically two main types of validations that I know of. 1 – surface edit types (can be done on the client) 2- remote queries (in need of the server’s or services’ assistance). When doing remote queries it is advised to use the async in javafx 1.2 (http://blogs.sun.com/baechul/entry/javafx_1_2_async)

    Hopefully, this can help folks consider those factors when creating a robust enterprise architectural solution.

    -Carl

  5. Pingback: Java desktop links of the week, August 24 | Jonathan Giles

  6. sj

    The source file is not complete. It does not run because main class is missing… build script does not work. did not want to download netbeans to run the app

  7. Per Lundholm

    Validation is different on server and client. The client can at best validate syntax while the server also can have semantic validation. E.g. “the user name is already taken” needs at least a round-trip to the server to find out.

    Good work, Carl!

Leave a comment