dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.19k stars 9.93k forks source link

Dynamic ViewModels #15889

Closed learner291 closed 4 years ago

learner291 commented 6 years ago

When will you be able to decide whether this can be a committed project, is there any timeline?

I noticed that Model needs to be defined at compile time in order for both server and client projects to utilize data binding. Is there any plan for having a loosely coupled models so that client is not aware of strongly type models and views can be created from dynamic view models?

We are investigating different frameworks for our multi-tenanted web application. The web application, patient case management system, requires data entry of multiple forms and as you can imagine each form can contain 100s of fields. Each tenant can have their own set of forms as well so adding new patient form may have 80 fields/controls for Tenant A and adding new patient for Tenant B may have 50 fields/controls, some of these fields may be common among tenants. Will/Can blazor have the functionality to handle above requirement i.e. only showing relevant controls/fields to relevant tenants and implement two-way data binding using MVVM pattern, if yes, is there any sample project?

haasey commented 6 years ago

https://learn-blazor.com/pages/dynamic-content/

Looks to me like you can do dynamic stuff.

danroth27 commented 6 years ago

When will you be able to decide whether this can be a committed project, is there any timeline?

Nothing to announce yet. In the meantime we continue to make progress on Blazor as an experimental project.

redowl3 commented 6 years ago

@danroth27 what will be the trigger point or measure for deciding whether the experimental tag gets dropped? Are you looking to the community to drive development, is there a technical issue or business reason behind the decision?

peter0302 commented 6 years ago

You can absolutely do dynamic client side view models right now. I'll try to post an example on GitHub when I have a chance.

I think Blazor could benefit from some built in MVVM support such as adding ICommand Command to buttons, and having BlazorComponent implenent INotifyPropertyChanged, but there's nothing stopping anyone from building an MVVM framework on top of what's already there.

legistek commented 6 years ago

+1 for hoping Microsoft will declare the project officially committed asap. I don't want you to run into a chicken-and-egg problem where developers don't want to commit because it's still experimental, but it won't go past the experimental phase until more developers commit.

We're ready to embrace this lock stock and barrel the day Microsoft does.

learner291 commented 6 years ago

You can absolutely do dynamic client side view models right now. I'll try to post an example on GitHub when I have a chance. I think Blazor could benefit from some built in MVVM support such as adding ICommand Command to buttons, and having BlazorComponent implenent INotifyPropertyChanged, but there's nothing stopping anyone from building an MVVM framework on top of what's already there.

@peter0302 That's great, please do so when you have a chance.

I'll appreciate if example is as close to real life scenario as possible. Obviously, I don't expect 100+ fields forms but if example can portray two forms each with different 3 or 4 fields with atleast one drop down control for showing look up values from a service/database and same view can dynamically create and display one or the other form with data binding (MVVM) depending on who (user) is logged in. The information about which fields to display for a user can be extracted from a database. Again, I am not expecting a full blown multi tenant example, just sufficient information for people to discern from the sample and replicate it for their scenarios.

Thank you

redowl3 commented 6 years ago

@legistek - sums up exactly what I am thinking in terms of chicken-and-egg scenario and of course ready to embrace Blazor as soon as possible.

legistek commented 6 years ago

Hey @learner291 let me explain what I am talking about being possible to make sure it's what you're trying to accomplish.

The MVVM pattern revolves around two key concepts in my mind: (1) property binding and change notification, and (2) commands.

Property change notification can be accomplished by having the view model and the view both implement INotifyPropertyChanged. The view model triggers the PropertyChanged event any time a notifiable property is changed in a setter via business logic. The view triggers a PropertyChanged event whenever some property of the View is changed (usually because of user input). Binding is accomplished - loosely speaking - by pulling the initial value from the source at the time of binding, and then having one or both parties subscribe to the PropertyChanged event of the other so they can dynamically update when the other does. (For one-way binding, the View would subscribe to the PropertyChanged event of the view model; for two-way binding, they would both subscribe to each others'.)

In a similar fashion, view properties that correspond to collections can look to see if the incoming collection implements INotifyCollectoinChanged and if so subscribe to it. Think of a list box bound to a list of people. The list of people would be implemented as an ObservableCollection<Person>, and the ItemsSource setter of the ListBox would see that the incoming collection implements INotifyCollectionChanged and so subscribe to the CollectionChanged event and update the ListBox contents any time the collection changes.

As for ICommand, this is even more straightforward. A component (like a button) exposes an ICommand Command property to correspond to a primary action - such as a click. The ViewModel exposes a property of an ICommand type, with the ICommand instance providing a delegate with the code to execute the command. The ICommand on the ViewModel is data-bound to the ICommand Command on the view. When some UI action occurs on the View, the View calls the Execute method of the ICommand'.

Sorry if you already knew all this. Point just being that this is what I'm assuming you want to accomplish, all this is 100% doable in Blazor right now, It can certainly get more complex - the binding mechanisms in WPF, UWP, and Xamarin Forms are all much more sophisticated than just subscribing to PropertyChanged, since they all support data contexts and compound paths - but at the end of the day it's all just based on reflection, interfaces, and events.

learner291 commented 6 years ago

Hi @legistek thanks for providing a detailed response.

I am familiar with MVVM and yes I do want to accomplish it in Blazor. However, I was looking for more extensive/dynamic example, consider following scenario in a patient case management system in a multi-tenant web application:

1) Hospital A has a patient registration form, lets call it Add Patient page. Hospital A would like to record Name (text box), Date of Birth (Date picker), Nationality (drop down linked to a lookup table in a database).

2) Hospital B also has a patient registration form and they would like to record Name (text box), Date of Birth (Date Picker), Ethnicity (drop down linked to a lookup table in a database), and Gender (drop down with static options).

Please note the differences in the fields in both cases above.

We'll have a super set Model called Patient with five fields (mentioned above in item 1 and 2). When a user from Hospital A clicks on Add Patient page link, can we create a dynamic View Model linked to Add Patient View with two way binding for Hospital A (or some other way to accomplish this in Blazor with two way data binding support) to show only three fields mentioned in item 1 above (the data about which field to show on Add Patient page for Hospital A is retrieved from database)?

Similarly, when a user from Hospital B clicks on the same Add Patient page link, can we create a dynamic View Model linked to Add Patient View with two way binding for Hospital B (or some other way to accomplish this in Blazor with two way data binding support) to show only four fields mentioned in item 2 above (the data about which field to show on Add Patient for Hospital B is stored and retrieved from database)?

For simplicity, I have mentioned only 5 fields for one form but in reality, there are different sets of forms for each hospital such as Add Patient, Triage, Careplan, Discharge, Risk Assessment etc and each form can contain 100s of fields, for instance a comprehensive Triage form can easily contain a couple of hundred fields for Hospital A and it can contain 50 fields for Hospital B and so on.

If we don't create forms dynamically then we will have to create static forms for each hospital so if our system is used by 100 hospitals that's 100 different Add Patient forms, 100 different Triage forms and so on which is obviously not a suitable solution.

To add more complexity, each tenant (Hospital) may have their set of validation/business rules for some of the fields as well, for instance, Hospital A may want to make Date of Birth mandatory but Hospital B may not want to make it mandatory (not a good example).

legistek commented 6 years ago

Aha. Could it be what you really want is templated components?

https://github.com/aspnet/Blazor/issues/404

Same view model, but different view templates depending on some variable?

learner291 commented 6 years ago

I'll review aspnet/Blazor#404 in detail to understand it properly.

If we have same view model, will it not use more bandwidth to download unnecessary fields, for instance if a Triage form has 200 plus fields for Hospital A but 50 for Hospital B, will it still download data for 200 plus fields for Hospital B, even though it requires only 50 fields and all other fields will be null any ways and will not be render?

legistek commented 6 years ago

Wouldn't that be up to the server-side code to not serialize properties that aren't needed? Or in your API request your view model would tell the server what to send back or not?

learner291 commented 6 years ago

I believe you are right, I was thinking more like client side model as in Angular but I understand what you are saying and yes server-side code can only send what's needed.

At first glance, it seems that aspnet/Blazor#404 should do the job but without seeing any sample I am not 100% sure.

SteveSandersonMS commented 6 years ago

Interesting discussion! It seems like this has wound up now, and it doesn't represent a specific outstanding work item, so I'll mark as closed. Please feel free to continue the discussion here if you wish though.

redowl3 commented 6 years ago

@SteveSandersonMS Apologies for hijacking the thread but any chance we can cover off the issue (at the top of the page) as to what will be the trigger point or measure for deciding whether the experimental tag gets dropped? Are you looking to the community to drive development, is there a technical issue or business reason behind the decision?

learner291 commented 6 years ago

@SteveSandersonMS I believe the outstanding work item could be defined as specific samples covering the scenarios mentioned above whether it could be done via templated components (#404) or some other way.

SteveSandersonMS commented 6 years ago

Apologies for hijacking the thread but any chance we can cover off the issue (at the top of the page) as to what will be the trigger point or measure for deciding whether the experimental tag gets dropped

Our PM is out for the next 2 weeks so that question might not move forwards much until he's back. But I'm hopeful that we're close to having some kind of answer.

redowl3 commented 6 years ago

Many thanks for the reply @SteveSandersonMS much appreciated.