I was inspired by article Decouple the View and its behavior to create a testable UI and wanna rework my JavaFX application with MVVM pattern.
(source: s-msft.com)
.
Ideally View Model should be View-independent and be testable as usual Java class. Also MVVM uses Command conseption to change View Model from View. So Command implementation is a part of View Model implementation and may be simply tested.
The questions is how Command should be implemented if it's result is a View changing? E.g. in View_1 I have a button after pressing on it a new View (e.g. View_2) should be created and shown. Should such Command be a part of a View and not be testable at all?
I think command could be testable. I'm not sure how exactly are you going to switch views, but AFAIK in JavaFX there is something like a Scene and Stage which is parent for scenes, is that right?
So in your command you pass the Stage as a dependency and on execution you set it a new scene.
And you can easily test it in unit tests - you can mock Stage and check if command passes correct scene to it.
I don't remember exact details of JavaFX but I would implement it like this:
class SwitchSceneCommand {
protected Stage stage;
protected Scene originalScene; // you can keep original scene if you want to have some undo-redo functionality
protected Scene newScene;
public SwitchSceneCommand(Stage stage, Scene originalScene, Scene newScene)
{
this.stage = stage;
this.originalScene = originalScene;
this.newScene = newScene;
}
public void execute()
{
this.stage.setScene(this.newScene);
}
}
Related
I'm new to using Java FX and was wondering if anyone could provide some answers for me on creating multiple independent stages. I'm also using Scene Builder for ActionEvents.
An example of multiple stages could be something like this:
Login page -> main stage -> other sub stages.
The way I understand is that you need a FXML loader on each controller to bring up each stage. So on my Main java class, under START method, I'll bring up a Login stage. And on my Login Controller, I'll have a method to bring up a Main Stage (i.e. once the Login button is pressed), and finally/similarly, I'll have a method under my Main Controller to bring up other sub stages, once a button to that sub stage is pressed from the Main Stage.
Is this the right way of opening new stages? Or do I have to have all the methods in one main class to open all the stages?
Thank you for any help.
no you don't need to have to have all the methods in one main class to open all the stages. you can create your new stages in other controller when they need to be built. for example in your login page, in the controller when a button is clicked And you want to go to main stage, code for the event be like:
FXMLLoader main = new FXMLLoader();
main.setLocation(getClass().getResource("adress"));
Parent mainParent = main.load();
Scene mainScene = new Scene(mainParent);
Scene currentScene = anItemOfProgram.getScene();
Stage stage = (Stage) currentScene.getWindow();
stage.setScene(mainScene);
and this is right way to create multiple stages
I am looking for feedback on why we should use one method over the other as far as loading and displaying new FXML stages.
Most of the time, I see tutorials and such that show the loading of a stage done from a separate class. However, it can also be done within the FXML file's controller itself, and I personally see that way as being a bit cleaner and more manageable.
Consider the following Main.java class:
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
// Method 1:
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout.fxml"));
loader.setController(new LayoutController());
stage.setScene(new Scene(loader.load()));
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
This seems to be the prevailing method. It creates the contoller and sets the Scene and then shows it.
However, if we change the start() method to this instead:
#Override
public void start(Stage stage) throws Exception {
LayoutController controller = new LayoutController();
controller.showStage();
}
and move the FXML loading code into the LayoutController constuctor, the result is the same:
public class LayoutController {
#FXML
private Label label;
private Stage stage = new Stage();
public LayoutController() {
// Method 2:
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout.fxml"));
loader.setController(this);
stage.setScene(new Scene(loader.load()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void showStage() {
this.stage.showAndWait();
}
}
The benefit I see here is more separation between the view and logic. Everything about the LayoutController and its associated FXML file are contained in one place.
So my question is this: what is wrong with the second approach? I am assuming it is not the standard method for a reason, but I'm not able to see any drawbacks.
Would a question like this be more suited to Code Review? I'm not really asking for opinions, as there seems to be a general "rule" that the first method be used.
In this case there is not much difference.
For larger programs the second approach is undesirable tough:
It violates the single responsibility principle:
The class is responsible for:
Creating the scene
Creating the stage
Displaying the stage (and even worse it does this in a way that may interfere with other logic (showAndWait))
It's the controller of the view and may become responsible for handling several events
Furthermore the way the class is designed in a way that prevents the responsibilities from being moved to other classes without issues.
In a larger program you likely want to create a class that manages passing data to views, arrange windows or display the view as part of a scene, ect. The second approach is ill suited for this.
Additionally it makes if harder to not repeat yourself. Unless you move the logic to a common supertype, you also need to implement the logic for showing the scene in every controller class. Repetition of the same or similar code results in code that is hard to maintain.
Note: Using a single class for loading the fxml and for use as controller is not necessarily a bad thing, but you should go with the Custom Component approach presented in Introduction to FXML.
I've studied all popular GUI patterns - MVP,MVC,MVVM and finally I decided to implement MVP (Supervising Controller). So I have the following OBJECTS(!). Stage<-View<->Model. It's important Stage!=View, it is another object. Between view and model data binding. Besides I have a presenter(controller) which handles all events and works with view and model, so View<-ViewInterface<-Controller->Model.
The problem is now how to get references to labels, textAreas etc in view. Javafx allows to use #FXML annotation to inject these components to controller. However, using MVP I need these components in View, as all logic for view is in View and I don't need them in controller. The only solution I know is:
public class MyView{
private Button button;
public MyView(){
...
button=(Button) root.lookup("#myButton");
}
}
That is to get references by their ID. However I don't like it. Or I do something wrong or I understand something wrong but I think a better solution exist. Please, help me to find it.
JavaFX has been designed to work with the MVC pattern. Hence it is much easier to use MVC than MVP. In MVP Presenter is responsible for formatting the data to be displayed. In JavaFX, it is done automatically by View. Here's a quick overview of JavaFX MVC:
Model - the domain data / data structure that you work with in your application (e.g. Person, Employer, Coursework, etc)
View - the UI definition of the application and its Model. The preferred way of creating a view is via an FXML file, which is essentially the View in JavaFX MVC.
Controller - the bridge between Model and View. The code is typically isolated in XController class (where X is the name of the FXML View). The instance of Controller is automatically injected by FXMLLoader or can be done manually in case you require a custom Controller. The Controller class will have access to UI (View) elements in order to be able to manipulate different properties and also the Model, so that it can perform operations based on the UI (View) input.
To sum up, in JavaFX you don't need to have class View, the View definition should be entirely in the FXML file. All UI elements should be injected with #FXML into your Controller class. If you absolutely have to use MVP, then AWT/Swing or MVP4j - http://www.findbestopensource.com/product/mvp4j might be a better option.
For more detailed explanation please have a look at the official Oracle tutorial for JavaFX: http://docs.oracle.com/javase/8/javafx/get-started-tutorial/jfx-overview.htm
If you require help building UI using FXML: http://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction_to_fxml.html
This tutorial covers basics of MVC in JavaFX and how each component communicates with others: http://code.makery.ch/library/javafx-8-tutorial/part1/
As an Android developer, I always use MVP pattern in my applications. MVC compared to MVP seems so old to me, so when I started working on a new Java app, I felt a little bit lost.
Here there is my solution:
Initial steps
In fxml files create the UI, without specifying a controller, because you don't need one.
Create the Java interfaces (IView, IPresenter and so on..)
Implement the IPresenter interface in the Presenter class, as you would do normally (do http requests, query a DB..)
Now the interesting part:
Adapting your view to MVP pattern
Let's see some code:
Create your GUI (for example a Main GUI) and implement your View interface
public class MainGUI extends Application implements MainContract.View {
public static void main(String... args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
//here we will load fxml or create the ui programmatically
}
//method from view interface
#Override
public void onServerResponse(String message) throws IOException {
//update the view
}
Now the last step:
Communicating with the presenter
To do this, we first create an istance of our presenter:
private MainContract.Presenter presenter;
public MainGUI() {
presenter = new MainPresenter(this);
}
this is, of course, the MainContract.View implemented in the MainGUI class
Now we have to get a reference to the view components
private ComboBox<Double> mySimpleList;
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("layout_main.fxml"));
Parent root = loader.load();
mySimpleList= (ComboBox<Double>) loader.getNamespace().get("mysimplelist_id");
...
primaryStage.setScene(new Scene(root, -1, -1));
primaryStage.show();
I prefer using fxml files instead of creating the ui by code, but the logic behind is identical.
Set the items
...
mySimpleList.setItems(ValuesFactory.getMyValues());
And the listener
...
mySimpleList.valueProperty().addListener(simpleListListener);
What is simpleListListener?
A simple ChangeListener, where we finally call a presenter method
simpleListListener = (ChangeListener<Double>)
(observable, oldValue, newValue) -> presenter.doTheLogic(newValue);
This is an easy scenario, but in principle this is how we can use MVP Pattern with JavaFX. I also understand that it isn't the definitive solution, so I hope that one day there will be more docs where I can learn more about this argument!
Let me know if I wasn't clear in some part of the code
According to the JavaFX documentation, a child stage is defined by
A stage will always be on top of its parent window.
The issue is I'd like to render them like normal windows according to each other (if you click on one, it will render above the other). Can I do this without a hacky workaround?
The only other functionality you get from making a stage a child of another stage is that the child stage is automatically closed when the parent stage is closed. You can emulate this with a listener:
Stage firstStage = ... ;
Stage secondStage = new Stage();
// secondStage.initOwner(firstStage);
firstStage.addEventHandler(WindowEvent.WINDOW_HIDDEN, evt -> secondStage.hide());
// ...
If you are relying on using getOwner() anywhere you would have to find a way around that.
(I'm not sure if this qualifies as a "hacky workaround", but it should work...).
I am getting my feet wet with javafx. This is what I am doing.
FXML Views
DI Controllers
Weld-SE Managed Services and Models
Trying to confine UI to FXML
Trying keep the Controllers thin
Problem:
While trying to code the UI, most static UI is confined inside the fxml. But there are scenarios where I find my self adding, removing, showing, hiding elements etc.
I find myself doing this inside the controller as fx lets me configure controller method in the view which it will call on a particular action / event. All this code deals with Dynamic UI building / manipulating and belongs inside the view layer. But, it ends up in controller making the controllers fat.
javafx provides javascript integration. This is one possible way to abstract that view manupulation code away. But this would add not so perfect javascript into the mix.
How would I abstract the code away in java or fxml so that I don't break the Thin Controller Paradigm ?
EDIT
#assylias
Agreed, I have thought about this and this way that java class and fxml together become a reusable widget. But then, how do I wire this into FXML. FXML doesn't understand anything but a controller. Let say I wire this view class into fxml using fx:controller and not name it controller. So I have something like this.
This view class has nothing but view manipulation code. Then I would create another controller class. But then I would expect to somehow fill the form data into this controller. This should only happen when the user has submitted the form. So in a way, I need to tell javafx somehow that UI manipulation request / event is different from actual data manipulation request / event.
Your thoughts, sorry if it was verbose. Tried to articulate it in as few words as I could.
I think the easiest solution to this is to remember that the controller specified in FXML is a view controller. It's purpose is to contain code to modify and update the view, not to contain traditional MVC controller code or business logic.
For example, in a project I'm currently working on, I'm using JavaFX with Akka Actors. The application is written in scala. The JavaFX view controllers contain any code necessary to modify the view. One screen contains a login form. When the user clicks the login button, the view controller simply creates a message containing the username and password, and sends that message to the actor responsible for doing business logic. If that actor determines there is an error then it will send a message back to the view controller, and the view controller can decide what sort of updates need to be made on the screen.
I've found that using akka actors with JavaFX greatly simplifies coding the application for at least two reasons.
Because using an actor system mandates sending messages between actors, there is a natural boundary between presentation code and business code. The messages that are passed back and forth form this natural boundary.
Using actors completely replaces the complexity of working with threads/tasks. It completely eliminates the need to code javafx.concurrent.Task's for long running processes.
How about putting your view manipulation code in Main Class ?
Main Class :
public class SampleJavaFXApp extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"SampleUI.fxml"));
Parent root = (Parent) loader.load();
Controller controller = loader.getController();
viewManipulationLogic(controller);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
// view manipulation logic
private void viewManipulationLogic(Controller controller) {
controller.getBlueButton().setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
System.out.println(" I am just about button!");
}
});
}
Controller :
public class Controller implements Initializable {
#FXML
private Button blueButton;
public Button getBlueButton() {
return blueButton;
}
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
//real data manipulation
}
}
cons : You need getters for all Nodes u want to manipulate , in controller class.