user2022068
user2022068

Reputation:

MVP, JavaFx and components references

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.

Upvotes: 4

Views: 8930

Answers (3)

Kostiantyn Perehuda
Kostiantyn Perehuda

Reputation: 36

I know this question is very old, but I still decided to write my answer here.

I will start by saying that despite being advertised as following MVC pattern, JavaFX/FXML really follows MVP pattern by default.

There are several good articles about MVC vs MVP (Baeldung - MVC vs MVP, GeeksForGeeks - MVC, MVP, MVVM). Here I will point key main differences.

MVC pattern

  • In MVC pattern the Controller is the entry point to the application. User Events are caught and handled by the controller. Take as an example a Spring MVC web app. The entry point to the application is the HTTP request handling performed by the controller. Sure, the user needs to click a button in the view in order for the HTTP request to be sent, but the button click isn't what triggers the backend server to act. It is the HTTP request sent by your browser as a result of your button click that triggers the web server to act. In fact, the user could've sent the same request by curl command for example. The view isn't needed to drive the interaction, it's not the entry point.
  • There is a Many-to-Many relationship between Controllers and Views. Triggering one controller action may result in different views being shown to the user depending on the state of the system. The Controller has a power to decide what view to show to the user. Coming back to the web app example, the controller might decide to respond differently to the same request sent by users with different access rights, and show different views as a result. In addition, the same view can be shown as a response to different requests (e.g. redirect).
  • In traditional MVC pattern, the Model and the View are coupled and can talk directly to each other. The View uses the propetries of the model to display. We don't see this thing often in modern web apps nowadays, especially when we refer to the model as the business logic side of our application. We can see Spring MVC using the concept of the 'Model' (which is really just a Map of key-value pairs) to pass the data between the Controller and the View. We can inject an instance of a Model (here I mean org.springframework.ui.Model to be extra clear, please don't consufe this with MVC Model) into the controller and use it to pass data returned by service/application layer (MVC Model) into the view. We can also pass the instance of the Model (org.springframework.ui.Model) object directly into service layer by the means of method arguments, or ThreadLocals, or by some other way, and achieve 'True MVC'. But people don't usually do it, because it couples Application Logic (MVC Model) and the View (this is the drawback of MVC that MVP attempts to solve).

MVP pattern

  • In MVP Pattern the View is the entry point to the application. User input is handled by the view, the view then forwards these user events to the presenter by calling its methods. This is what we see In JavaFX. When user clicks a button in the view, the click event is handled directly by the button in the view (scene graph). We can wire a callback and make the button node call some method of the Presenter (FxController) associated with the View.
  • There is a One-to-One mapping between the View and the Presenter. In JavaFX we can only set one controller for the specific Parent Node.
  • In MVP the View and the Model are decoupled and the data can only be passed between them through a mediator/presenter. This is what happens in JavaFX/FXML. FxController can access the view and update the scene graph, we can inject nodes from the scene graph generated by parsing fxml into the FxController. And we usually don't expose JavaFX scene graph to our core application directly, we do it via FxController.

Overall, JavaFX/FXML is very flexible. It has poverful event handling and data binding functionalities, so you can go fully event driven and end up with MVVM (one might argue that JavaFX/FXML follows MVVM by default). Or you can be more traditional and end up with MVP, you can also start exposing your JavaFX data bindings into the core app and end up with a blend of MVC and MVP, but traditioanlly JavaFX/FXML follows MVP. Just because JavaFX uses the word "Controller" doesn't mean it follows MVC. The same is true for Angular framework, for example. Angular follows MVVM, but it uses the term "Controller" to refer to the ViewModel.

And one last thing, it's not mandatory to have ViewInterface in your app for it to follow MVP;)

Upvotes: 0

Giuseppe Giacoppo
Giuseppe Giacoppo

Reputation: 435

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

Upvotes: 3

AlmasB
AlmasB

Reputation: 3407

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/

Upvotes: 9

Related Questions