Workflow and multi binding with #D.MVVM!
In every example for MVVM, you find the three blocks diagram explaining the binding between the View, the ViewModel, and the Model.
If you dig deeper into a real-world application, you may use the 1:1:1 relation, but in most cases, you need it very differently.
1:1:1 means One View connected to one ViewModel, connected to one Model!
Let's start with the View:
Most of my views are not simple TForms but much more assembled on small subframes. The subframes are also TForms and not TFrames, because of the recurring problems with frames (in the past). But that is not the topic.
The View will be created in the composition root - or elsewhere - and then?
Every subframe must also be created and placed into the target frame of the parent view.
So far so good. Of course, this is all handled by my framework.
In some cases, every frame should have its own ViewModel. But what if not?
Well, keep this in mind and start at the other end of this chain...
We have a database where every row is just an entry of our beloved TPerson, with the normal address fields.
Part of this TPerson is a list of telephone numbers handled in a different table and perhaps also a list of bank accounts also in a different table.
As we all know - I hope - the Model is not our Database interface, the Model will get the interface to the database for local or remote storage. This could be an interface to a local database or to a RESTfull web service or something else!
Let us talk concretely for the moment:
To store a value into a database or send it as JSON you need the field-value. For the complete row and perhaps for the subtables you need a list of field-values and the field-names or at least the index position in the param list of the query.
We will name this: Place X.
Place X need the Name, Street, Zip and more... And of course, the two lists for the subtables.
To be able to get this right you have to collect the data. A good place would be the Model. Wait... The Model?
So for all the different fields and from at least 3 frames, we have to collect the data in only one Model!
To store the data, the database should do a transaction to keep the subtables in sync with the address table. This would be a nightmare if 3 Models would do this separately.
At this point, we have our View with 3 subframes (Address, Telefonlist, Bankaccountlist) and only one Model.
1(3) : ? : 1
The View is not the place to collect the data from the subframes. Should we bind the subframes to one ViewModel?
In this case, we get our example relation of 1:1:1. That would be nice, but will it fit our needs? One reason to separate our View into SubViews is the possibility to swap out a subview to a different version. To check if the subframe data is sufficient to be stored, I would like to have a simple ViewModel per frame. Let's collect:
PersonView -> PersonViewModel -> PersonModel <- Database/Rest Interface
AddressView -> AddressViewModel -> ?
TelefonView -> TelefonViewModel -> ?
BankView -> BankViewModel -> ?
The PersonViewModel has no fields (or just two buttons).
Even if we do not store data in visual controls, we must define the controls. Lets name this Place B,C,D for the three subviews, and Place A perhaps for the PersionView that only has a clear and save button.
In the PersonViewModel we have to declare a Field/Property canSave (Place A1). Inside the PersonViewModel we must be able to check the three other ViewModel so every ViewModel from the subviews should be connected to the "parent" ViewModel.
In the AddressViewModel we have to declare the Field/Properties for the Address. (Place B1) And the same for the two other ViewModels (Place C1, Place D1).
In a best-case scenario, we would skip the Models for the subviews - if not we would also have Place B2, C2 and D2.
Is MVVM slower in development because you have to type everything three times?
Don't forget we're doing MVVM to get things faster developed!
How can we archive this?
Let's take a look at the full development cycle and for simplicity name it in Delphi terms:
Result := TPersonView.Create;
1(3) : 1(3) : 1 chain!
Let's compare RAD vs. MVVM!
How can we archive this?
Create the database with just one definition: 4:0 for #D.MVVM.