sialcasa / mvvmFX

an Application Framework for implementing the MVVM Pattern with JavaFX
Apache License 2.0
489 stars 105 forks source link

Connecting two view models to a single view #610

Open chuuck opened 4 years ago

chuuck commented 4 years ago

I am trying to find a way to bind two view models to a single view, however, I cannot find a way to achieve my goal.

manuel-mauky commented 4 years ago

This is not possible. A single ViewModel can be used by multiple Views but each View can only have a single ViewModel. The idea is that from the perspective of the View it shouldn't matter how the ViewModel is implemented. But you can inject other ViewModels into the one ViewModel of the View and pass properties through the main ViewModel so that indirectly the View is working together with multiple ViewModels. This is then an implementation detail of the ViewModel which is OK.

lqyaos commented 2 years ago

But you can inject other ViewModels into the one ViewModel of the View and pass properties through the main ViewModel so that indirectly the View is working together with multiple ViewModels.

How can I achieve this elegantly? The problem I have now is: I have a lot of functions in a single interface, and I can't write them all on the same viewmodel. I need a form similar to fragments.

manuel-mauky commented 2 years ago

The injection itself isn't depending on mvvmFX but depends on your dependency-injection framework.

I'm not sure what you mean with "fragments". I have the feeling that your issues are not a mvvmfx issue but a more general software architecture/design issue. Having lots of functions in a single interface might be not a good idea (possibly a violation of interface segregation principle).

The best way that OOP provides is composition: Implement the functions that belong to each other in one or multiple classes, create/inject instances of these classes in your ViewModel to use the functions. But the actual design highly depends on your specific use-case.

lqyaos commented 2 years ago

Sorry, I may not express it clearly. What I mean is that I want a view to be divided into multiple fragments, and then there are multiple viewmodels to manage each fragment, that is, one view corresponds to multiple viewmodels.

manuel-mauky commented 2 years ago

Ok, I understand. This is possible with mvvmFX the way I explained above. In mvvmFX, normally there is a one-to-one connection between View and ViewModels. This is the usual case but it's also possible to have one ViewModel which is used by multiple Views. What is not possible is to have One View to use multiple ViewModels. One View always knows exactly one ViewModel. However, the way this ViewModel works internally is hidden from the View. The ViewModel itself internally can use whatever other classes, services or other ViewModels without the View even knowing. It's an implementation detail and this kind of architecture gives you the freedom to refactor the ViewModel without having to change anything in the View. For example, immagine you start with a big ViewModel with lots of exposed properties which are bound by the View. Later on you deside to refactor this by extracting parts of the logic and properties to other classes (name them ViewModel or whatever). You change the implementation of your ViewModel without changing it's API and it's observable behavior.

A small example:

// before

class MyViewModel implements ViewModel {
     private StringProperty firstname = new SimpleStringProperty();

     private StringProperty lastname = new SimpleStringProperty();

     public StringProperty firstnameProperty() {
        return firstname;
     }

     public StringProperty lastnameProperty() {
        return lastname;
     }
}

// after

class MyViewModel implements ViewModel {
     private PersonViewModel personVM = // ...

     public StringProperty firstnameProperty() {
        return personVM.firstnameProperty();
     }

     public StringProperty lastnameProperty() {
        return personVM.lastnameProperty();
     }
}

This way you can re-use the "PersonViewModel" in the example.

The intersting point is, how you get the instance of the other viewModel. This is were dependency injection comes into play but this is beyond mvvmFX. mvvmFX doesn't provide general DI but only for objects managed by the framework itself.