This is a more complex example application in which you can manage contacts.
It uses mvvmFX and mvvmFX-CDI. Additionally the following third-party libraries are used:
- FontAwesomeFX for the icons
- ControlsFX for the validation decorators
- AssertJ-JavaFX for easier testing of observable values in unit tests
- DataFX for loading XML files
- Advanced-Bindings to simplify some bindings
- JFX-Testrunner to run Tests in the JavaFX Application thread
The application has a master-detail view. In the master pane there is a table of all contacts. When one contact is selected, the detail view will show the properties of the selected contact.
With a dialog you can add new contacts or edit existing ones.
mvvmFX Scopes are used for two scenarios in this example:
- Communication between the Master and the Detail View
- Dialog to add or edit a contact
In this scenario the MasterDetailScope is used to provide a property where the MasterViewModel.java signals which contact should be displayed in the DetailViewModel.java.
public class MasterDetailScope implements Scope {
private final ObjectProperty<Contact> selectedContact = new SimpleObjectProperty<>(this, "selectedContact");
}public class MasterViewModel implements ViewModel {
@InjectScope
MasterDetailScope mdScope;
public void initialize() {
mdScope.selectedContactProperty().bind(selectedContact);
}
}public class DetailViewModel implements ViewModel {
@InjectScope
MasterDetailScope mdScope;
...//Create all bindings to the information of the mdScope.selectedContactProperty()
}In this case the scope is used to handle the state of a multi paged dialog.
To understand the machanism of the implemented dialogs, you should check the following classes:
-
Both of them are using the ContactDialogView with different configurations.
-
AddressFormViewModel and ContactFormViewModel are the dialog pages (1 and 2) that are displayed in the EditContactDialog and the AddContactDialog
-
Also check the Views where the dialogs are created (ToolbarView.java and DetailView).
The used scope is called ContactDialogScope and it has three use cases:
-
Configuration (eg. title) of the ContactDialogViewModel from the EditContactDialogViewModel and AddContactDialogViewModel.
-
DetailViewModel sets the Contact object that will be edited into the scope. This information is used by the dialog pages: AddressFormViewModel and ContactFormViewModel
-
ContactDialogViewModel binds the disableProperty() of the navigation buttons to the validation state in the scope. This validation state is bound to the validation state of the dialog pages (AddressFormView and ContactFormView).
There are resourceBundles available for german and english language. In App.java a global resourceBundle is defined for the whole application:
...
@Inject
private ResourceBundle resourceBundle;
@Override
public void startMvvmfx(Stage stage) throws Exception {
LOG.info("Starting the Application");
MvvmFX.setGlobalResourceBundle(resourceBundle);
...
}In addition for the menu a specific resourceBundle is defined in the MainView.fxml via fx:include:
...
<fx:include source="../menu/MenuView.fxml" resources="menu"/>
...This resourceBundle is merged internally with the global resourceBundle so that the menu can access both resources.
In the dialog for adding/editing contacts the mvvmFX validation feature is used. In ContactFormViewModel.java you can see how to define validation logic. In the ContactFormView.java the connection to the UI is done. This way the aspects of validation logic and validation visualization are separated.
In the ContactFormViewModel.java the mvvmFX ModelWrapper is used to connect the Model and the ViewModel layers with reduced code size and coupling.