Lets say I have a view that accepts a Person object.
Has three properties, FirstName, LastName, Age
Now lets say I add another textbox field that's not part of the object.
I don't need the value of the textbox, its just populated with data that's for the user.
When you edit the fields and post the Person to the controller, lets assume there is a validation problem so you return the Person object back with Errors
The problem is now the additional textbox has lost it's value since its not part of the model.
So I made a ViewModel with a string property for that field and a Person property to keep all the values. Seems like there would be a better way to keep the value in the "special" textbox?
You should be able to get that extra field from the posted fields. How do you set it first time, through the ViewBag? You should be able to set it again.
But what exactly is wrong with using a ViewModel? Sooner or later you will have 2 or 3 extra fields, or a Person and an Appointment.
I think that's totally the right way to do it. The viewmodel is the model for the view not the model for your non-UI processing, it contains a Person and extra viewable information. It fits exactly with the concept. Your Person is presumably a (non-view) model and therefore when you have a valid post back, you get the Person to save it's data (or whatever) and the extra viewable information is irrelevant at that point, because you are no longer in a 'View/UI' part of your app.
Make the view strongly typed to your viewmodel and access the Person within it
#model myViewModel
#Model.Person.FirstName
#Model.OtherViewOnlyValue
Go with the viewmodel, so much cleaner than ViewBags/Session/ViewData etc.
There are many times that you might think that you do not need to include a UI mapping to a ViewModel but most of the time you will end up adding the mapping into the ViewModel. I believe that ViewModel should represent everything on your UI screen. Since HTTP is stateless the post form values will play an important role in populating the user interface controls.
Related
After working with MVC for a long time, I decided to go with MVVM. I understood the basics of the pattern and got through multiple articles that explain that MVVM is waaay better then MVC any day. And I am okay with that.
I decided to make my own app in order to set my mind correctly for logic behind MVVM. So I created basic app that does follow MVVM principles and after a while I found the problem that you see in the title.
So, basically, this is the problem. Let's say that I have one object, call it Person. Person have name and surname. But when I want to show details about that person, I will have address, phone numer etc. Because one person can have many phone numbers I will have something from API that link to the user ID.
So we came to my question. If I have some basic information about some model, and want to have detail information about that same model, where do I keep ID (or link) for that detail information? Do I have to keep it inside view controller, which would be just wrong? Or do I keep it inside view model, even if I don't use it really on user interface?
The ID also belongs to the model class. ie If you have an object Person then simply create a data class Person, which will obviously include all the members say ID, Name, Address, Number and so on. You can identify each person using the same ID as well.
The View Model need not always know about the ID. If you have a list of Person objects in view model, then you could easily map each item using the ID. Additionally if you want to have currently selected item or something, you could map it to viewmodel property of that object type ie Person. So you need not keep a PersonID field in ViewModel unless it is absolutely required for some rare cases.
Sorry, but I did not understand this : So basically in prepareForSegue method I could say something like give me from current VM object at particular index and create VM for new view that I will actually send ?
As far as simple applications are concerned, the above approach is more than enough. But in some rare cases, you may need to keep the current selected item's ID in the view model. So if you're using a list and keeping a property for selected item, it may not be the type of that list ie Person. Instead it could be the ID alone.
Hope you got the point.
Is it possible to safely programmatically get a list of fields that are in the View that has just posted back to a Controller?
I noticed a problem with the default implementation of the scaffolding, in
DB.Entry(model).State = EntityState.Modified
DB.SaveChanges()
The problem is that if I haven't included a field to be edited in the view, it is being overwritten by the default value of the field that .NET assigns when creating the object. eg. If I have a User class with ID, Email and PasswordHash and I want to allow the user to update their Email address only, if I don't include anything for the PasswordHash field, it is reset to NULL as it is passed into the controller as NULL. At the moment, I am working around it by retrieving the current object from the database and updating only the fields which I know are in the View from the model passed in. That isn't such a problem for a small table, but I would like to have a general solution that I can apply across the board, especially for large tables which may during development and I don't want to have to update the code every time.
I know that I could loop through the POST variables and examine them to see what has been posted, but that creates a security issue as the user could inject additional fields that I don't want them to edit. I suppose I could explicitly exclude ones that I don't want them to edit, but then again, I would rather not have to list those if I can avoid it as it is an extra thing to maintain.
I think that there are 2 problems here and I'm not sure either are solvable...
Getting the View that posted back
Establishing which fields are included in that View (I might need to construct it again temporarily to do that?)
I suppose that I can probably get away with ignoring the first one as I could just only ever use that method on the Controller for a single View. That is still a little less neat than I'd like, but it does reduce the issue to just establishing which fields are in the View.
If a view needs only certain properties, create an interface with only those properties. Use this interface in the HttpGet and HttpPost methods.
And then you can use something like AutoMapper to map the viewmodel to your entity.
Say I have a domain level "Person" class that contains a lot of properties(FirstName, LastName, Age, Address, Telephone, EmailAddress, etc). For the purposes of a view, I only need to pass the Age property. The smaller, the better as the collection is being passed to the client as a JSON string.
What is the best way of managing this?
1) Do I create an anonymous type collection and pass that to the view?
2) Do I create a new "ViewModelPerson" type that only contains the "Age" property.
3) Do I create a new domain "Person" super type and have my Person and ViewModelPerson derive from it (seems a convoluted way of doing things).
Then, whats the best way of persisting these details onto my server (ie passing the age value into a collection of Domain Person objects?
EDIT:
Apologies, I should have said that I'd be returning a collection of Person objects (each with just an Age property).
1) I do not think that is possible, please elaborate
2) Yes! I would call it PersonAgeViewModel though.
3) Very convoluted indeed unless you know that you will derive from Person a lot and are planning to implement TPH or TBT in the database anyway.
If you're interested in only sending the Age (a single age) then don't specify a Model in the view at all. Add the value of the age to the ViewBag.
Check out Hajan's Blog Entry.
Call me pedantic, but I would lean towards creating a ViewModel with a single age property. I'm not sure the ViewBag approach would work unless you're building the JSON in a view. Typically when we return JSON we just use
return Json(model);
I would definitely recommend against a common base type between domain and view models.
I'm trying to google for info on a situation, but I dont know what it is called, so its hard to find results :)
I have a model with say 10 fields. But only some of those are shown on a particular view, lets say 3 of them: id, name, date. What do you call this kind of view that does not display the whole model? A partial view?
The problem is that because 7 fields are not sent to the view, when the Update action is called on the controller, those fields are null, and the DB gets updated with those 7 fields set to null.
This is called a ViewModel, which is obtained from the Model and is more adapted to the View.
This is still a view.
You are not specifying what kind of storage are you using, I'll make an example using the entity framework, but you can do it with whatever method you like.
The model for the view is an Entity. When you display the form in your view, only part of the fields in your model are editable. When the user submits, therefore, your model has only several fields filled in.
So, you should retrieve a new copy of the object you are editing from the database (call it "fromDb"), copy only the edited fields into the fromDb object, and save the fromDb object instead.
This way, all the fields are preserved.
Another way to do this, is to render hidden fields for all the fields that are not present. However this is NOT secure, as the user could edit those fields by hand (using the developer tools, or firebug).
A View accepts one Model.
But I need to draw HTML input controls for two models.
An example will illustrate:
I have a screen where I add Employees.
After adding their first name, last name and so on, I need the user to choose a number of Companies the Employees could be in.
The Companies are in one table.
The Employees are in another.
And a linking table joins them.
So it seems I need to pass the Companies to the View.
Can I pass multiple models to the view?
Or do I have to do an ugly database lookup in the View to find the Companies and manually spit out HTML for checkboxes without HTML helpers?
A Model doesn't have to consist of just one object or a single collection of one type of object. It can contain many objects and/or collections of objects. It seems that the model required for your page consists of at least collections of both employees and companies. If you have no type which fits this bill in your business object abstraction then you need to create a ViewModel for this page which can do the job.
This answer may help to explain how a ViewModel fits in MVVM ViewModel vs. MVC ViewModel
This is not entirely obvious - I'm sure it is to some guru types but for the rest of us trying to work things out its a bit more interesting.
Without going into detail I see a number of ways of solving this:
You need both sets of data so you need a view specific View Model
Your model is the Employee but you can still add other data to the ViewData - so make the Employee the model and pass the company data in as well as view data but not part of the model
(You may want to do this regardless of what model you use) Render the company selection elements as a separate view - which is where things get interesting - you obviously have to pass the existing selection, or a means to identify same, to the component view but do you have to pass the company list or could you, at this point, cheat a bit (prgamatically)? My feeling is that this is a partial view and that you need to pass it a company selection model (list of companies with selection indicated) but you could in theory do RenderAction and have an action that returns a view that goes to get the company selection for the specified employee - that way the overall view never sees the company data, that becomes a separate encapsulated problem - at least in terms of loading the data.
I think in this case where you're adding the employee either tweaking the model or adding the company list as supplementary data to the ViewData is sufficient, but for editing - assuming its required - rather than inserting you want a company selection list (all the companies with flags to indicate which are currently selected) rather than just a list of companies and at that point it all gets a bit more interesting