How page specific should a viewmodel in MVC be? - asp.net-mvc

I've understood that a viewmodel in MVC is supposed to reflect data on a single page rather than objects in the model. But should the viewmodel correspond to the data you want to show on that page or to the data you want back from that page? If we for example look at a login page then I just want username and password back in the post, but I might need more variables than that when displaying the login page (previous error messages etc).
Should the viewmodel then just contain username and password as parameters and the rest of the variables end up in viewbags. Or should the viewmodel contain all values I want to show even though I'm only interested in a few of them in the response.
What is best practice when using viewmodels?

All data that somehow interacts between the html and your server should be in a ViewModel.
This allows you to perform formatting and such outside your html and inside your ViewModel properties.
However, if your page contains a lot of controls or data, you may want to split it into multiple ViewModels (example one for the Get and one for the Post).
The post model may contain only data that you entered and needs to be validated.

I think it's best to put everything in the view-model. This keeps the code cleaner and makes discovery and maintenance easier as well. The view-model should be your primary mechanism here.

I would say only properties you need, in your case username and password. If you want to display error messages then that's what ModelState is for. You can always append any error messages to your ModelState:
ModelState.AddModelError("PropertyName", "Error Text")
Beyond that let's say you have a form that contains a list of categories that you need to pick one category from a drop down. In this case I usually attach that list to my model even though the only thing being submitted is the actual selected value. But this is a matter of preference, meaning I could also set a ViewBag to contain this SelectList of categories and then bind that to your DropDownList. I suppose it's better to place this in a model because ViewBag is dynamic and you will have to cast anything in the ViewBag into it's underlying type on your views.

Related

Html.ValidationMessageFor( MyKey ) without being tiedto a model's attribute?

My model does not really represent what my form is posting. Example my Orgs Model which holds orgs helps me generate a treeview the users selects several nodes of the orgs tree and submits a form. The form posts an array[] or org ids.
(maybe i'm doing this all wrong, please let me know tried binding to models and that was confusing when dealing with trees grids etc and using partial views and ajax returning partial views and editorfor's etc.. the default model binding was useless)
anyways back to my point, since I want to validate if any orgs get selected:
if (SelectedOrgs == null) //array[]
{
ModelState.AddModelError("OrgsNotSelected",IValidationErrors.OrgsNotSelected);
}
my question is how do i retrieve this random key that i just made up from my view? my model and even my viewmodel do not have an array for the selection this is just the result of the post.
I'm not sure what to do in the view to get the value for "OrgsNotSelected".
Thank you!
Bilal
If you were doing normal submit actions to your controller, you would need to use the ValidationSummary to display errors that are not attached to a specific property.
As you are using Ajax, you would be better off returning a json result from your controller and you can define this so that it includes your errors in a format you can use in the success function to display your messages.

Mvc How safe is viewbag?

My website is built around tabs. I have one single page with multiple partial views that display each tab.
The problem im facing now is I want to loop through files that the user has uploaded and display them in one of my partial views. This requires me to send the file list as a paramater in my action like this:
//Uploadedfiles is a function that adds the files to a list.
var files = UploadedFiles();
return View(files);
Because im only using one view to display all my partial views, i get:
The model item passed into the dictionary is of type 'Microsoft.WindowsAzure.Storage.Core.Util.CommonUtility+d__0`1[Delamapp.CloudStorageServices.UploadEntity]', but this dictionary requires a model item of type 'Delamapp.Models.LoginFolder'.
This means im required to not send a model item to my index view. Now, the only thing i can think off is adding my file list to viewbag and then display them on my view. BUT.. The files require high security. How safe is viewbag? Can you for example store sensitive login information in there? Can you think off some other way to accomplish this?
Thank you in advance
You can pass a model item to your view, but the model item you are passing doesn't match the type of model that your view uses (that's what the error message says).
So you need to do one of the following:
Modify your view to accept the model type that you are passing to it
Put the data you want to pass into the model type that your view is expecting
Create a new model type for both your data and for the view, and use that
In terms of security, I don't think using a view bag versus model binding really enters into the question of security. Both are just ways of passing data in between the controller and the view, and that all takes place within the ASP.NET process (perhaps you have ViewBag confused with Web Forms' ViewState?).

MVC required fields on optional object

I have a complex view model which has some subclasses. For the sake of a simple example, let's assume there's one subclass.
That subclass is displayed by rendering a partial view. The user can toggle that view in the interface, completely hiding the whole thing.
Now there are two valid input options:
The user chooses to hide the partial and doesn't fill in anything. The whole thing should be ignored.
The user chooses to view the partial, now he has to fill it in. Some of the fields are required, some are not.
What's a good way to handle this is MVC validation? I cannot mark the fields as Required, since the postback will post the empty values and the server side logic will say ModelState.IsValid is false beacuse there are fields missing.
Of course I can lose the [Required] attributes and do the checks manually myself, but I was wondering if there's no better way to get the same result.
The simplest way of doing this is like this ASP.NET MVC 3 Data Annotation: Add validation dynamically.
For a harder (a bit nicer) solution read this: DataAnnotations "NotRequired" attribute

ASP MVC3 Pass additional params with "return View(model)"

I'm in serious need of passing url params with View class. Here's code:
if (!ModelState.IsValid)
{
return View(model);
}
This should not only return model based view, but also add specific param to URL (param won't change view details, but is needed as it's one of few automatically generated SessionKeys (one for each tab/window used to view app) and I know no other way to get to it, different than passing as param (it can't be generated everytime, 'cos params will change; it can't be global variable because it'll reset its value each refresh; it can't be static, because static is evul).
Oh this action is called with use of form and submit button, not actionLink or something like this.
EDIT1: I need params to stay in URL after refresh, or I need some other form of keeping data that persists through refresh/validation fail.
If I understand you correctly you have data that you need to use in generating Urls on your page? This just forms part of your ViewModel - or at least it should, since it's data that the View needs in order to render.
You can use ViewData to add any extra data that isn't part of your view model. Or, better still, add the data as members to it. Equally, if different views with different View Models require this data, add a ViewModel base class and derive from that so you can share that data.
use
RedirectToAction("actionName","controller",
new RouteValueDictionary(new {param1="value",param2="value2"});
or you can use hidden field to store the values in your page and then pass this down as and when you need them..

Passing data from Controller to a User Control View with ASP.NET MVC

I have a View class (OrderView.aspx) which shows the details of an order (Account Name, Order Date) as well as a list of order lines via the Repeater control. Each order line is represented by a User Control View (OrderLineView.ascx) which shows the details of the order line (Item Name, Quantity, Price). I have a model object called Order which I use as the data source for all of this, which I pass as the model object for the view.
Each OrderLineView user control has a Save and a Delete button. I have the Save button posting the contents of a form within the OrderLine control to a Save method on the Controller and then RedirectToAction back to the same Order page (this refreshes the whole page, nothing AJAXy about it). I have the Delete button linking to a method on the Controller that tries to delete, and then RedirectToAction back to the same Order page. If the delete fails, however, I want a little error message to show up next to the delete button when the page renders again(remember, there is a delete button for every order line on the page, so I only want the message next to the one I clicked). My questions:
1 - How do I pass this data from my Controller method to the specific User Control? Should I somehow add it in to the model? Seems like a bad idea (since it really isn't part of the model).
2 - Should I have a OrderLineController for the OrderLine operations as well as a OrderController for Order operations? I just want to know if best practice is to have a separate Controller for every view.
3 - I have seen how some people might call RedirectToAction with an anonymous type value like this:
RedirectToAction("ViewOrder", new { Id = 1234, Message = "blabla"});
but this makes the Message value show up in the URL string. I am OK with that, but would prefer that it doesn't show if possible.
4 - Also, for accessing properties of the Model from within the view, I find myself doing this all of the time:
foo(((someModelType) this.ViewData.Model).SomeProperty);
I don't like this for a number of reasons, one of which is the fact that I don't want my view to be coupled with the type of my model (which is why I am using ViewPage instead of ViewPage). I would much prefer to be able to have a call like this:
foo(ModelEval("SomeProperty"));
Is there such a thing? I have written my own, but would like it if I didn't have to.
1
Check out ModelState.
ViewData.ModelState.AddModelError("something.Name", "Please enter a valid Name");
ModelState is actually a dictionary, so you could identify the errors on a per-control basis. I don't know if this is a best practice, but it would probably work.
Try something along the lines of
ViewData.ModelState.AddModelError("something#3.Name", "Please enter a valid Name");
and in your view, you could put
<%= Html.ValidationMessage(string.format({"something{0}.Name", YourUniqueId))%>
4
You can strongly type your view, so you don't need that cast, but if you're concerned about tightly coupling, this may put you off. But having the strong type there is no more tightly coupled than having a magic string point to that property of the model anyway. The former just gives you type safety and the glory that is intellisense.
Since your OrderLine has a unique ID you can use that to construct a key to be placed in the ModelState errors container.
public ActionResult Delete(int? Id)
{
ModelState.AddModelError("OrderLine" + Id.Value, "Error deleting OrderLine# " + Id.Value);
...
}
and then use the ValidatinoMessage helper. This will check the ModelState to see if an error exists and if it does it will display the message. Otherwise it's blank.
<%= Html.ValidationMessage ("OrderLine" + Id)%>
In the next release of MVC Model will become a top level property so the following
foo(((someModelType) this.ViewData.Model).SomeProperty);
can be written as
foo(Model.SomeProperty);
Model objects should already be typed unless you're using public object as a property?

Resources