I'm pretty new to MVC, ASP.net, and, well, server-side scripting in general.
I was watching a tutorial video at www.asp.net/mvc where the person was explaining a template. He explained that the controller using viewdata to send information to the view.
Correct me if I'm wrong, but I believe it was used like this:
CONTROLLER: ViewData["PropertyName"] = value;
VIEW: <p><%= ViewData["PropertyName"] %></p>
Is this correct use?
What would be a better way to do it, instead of using ViewData, and what's bad about it?
There are very view situations that I would advocate the use of the ViewData collection for.
For the most part, I would use Strongly Typed Views with individual View Models for each View.
Rather than using the ViewData a better approach would be to create a new Model object and pass that to the View which is strongly typed to the Model.
Model (Models/MyModels.cs)
public class MyModel
{
public string PropertyName { get; set; }
}
View (View/My/Index.aspx)
<%# Page Language="C#" Inherits="ViewPage<MyModel>" %>
<p><%=Model.PropertyName %></p>
Controller (Controllers/MyController.cs)
public class MyController : Controller
{
public ActionResult Index()
{
MyModel model = new MyModel()
{
PropertyName = "My Property Name"
};
return View(model);
}
}
Html.Encode can be used like this:
<%=Html.Encode(someObject) %>
or this if you're using ASP.NET 4.0
<%: someObject %>
Justin is correct regarding ViewData usage, as well as using View Models (This is definitely the solution that will probably most fit your needs).
The Session is another option, but it can tend to be a slippery slope, but you did ask for alternatives.
ViewData is good for complete random data that you're not sure what you're going to need.
If you're constructing well-defined views you'll want to use Strongly Typed Views. These let your view inherit from a particular business object (or, more usefully, a ViewModel) to display the data. In this situation, instead of ViewData["SomeProperty"] you could have access to a strongly-typed Model member ie Model.SomeProperty.
Also, Html.Encode is meant to be put around data elements that repeat user-entered data. It is meant to prevent HTML injection.
Related
I'm currently developping a website on asp.net MVC 4.
I'm a bit confused about the different ways to pass data from the controller to the view.
First of all, if we have a list of objects users, what's the difference between passing this list to the view using:
return View(users);
and
ViewBag.users = users;
My other question is about the first solution.
If we use this solution, do we have to use this
#model IEnumerable<mydb.users>
in the View?
Or could we use for instance
#model IEnumerable<mydb.registrations>
I know it would be odd to use a different model in the view than what we've used in the controller, but VS doesn't seem to be bothered.
Thanls a lot for you answers
You can pass parameters as you want, but the best way is to make your own "view model" for each view.
public class UsersViewModel
{
public IEnumerable<UserViewModel> Users { get; set; }
public int UserCount { get; set; }
}
Then pass this view model back to the view:
var viewModel = new UsersViewModel();
// ...
return View(viewModel);
You can use Automapper tool to automatically convert your entities to viewmodels and back. It will look like this:
// in Global.asax.cs on Application_Start
Mapper.CreateMap<User, UserViewModel>();
Mapper.CreateMap<IEnumerable<User>, UsersViewModel>();
// in your action
var viewModel = Mapper.Map<UsersViewModel>(mydb.users);
Your view model will be created automatically, check automapper docs for more info. Good examples on Automapper usage are available in RacoonBlog.
ViewBag is a container. You can pass anything to the View using the ViewBag say it a string or class or whatever. You can use any no of ViewBags to pass to view from controller.
return View(users); here you have the list there and you can pass only one object as model from controller to view.
The reply to the second question you can receive the object Model to View using #model where we use the reference to a Object in particular which is generic. The controller helps in identifying what is being passed to the view. You can use it in further coding using Model in your View. ex: Model.Users
I have a view model like this:
public class EditVM
{
public Media.Domain.Entities.Movie Movie { get; set; }
public IEnumerable<Genre> Genres { get; set; }
}
Movie is the real entity I wish to edit. Genres is simply present to populate a drop down. I would prefer that when I call:
#Html.TextBoxFor(m => m.Movie.Title)
inside my strongly typed view that the input control have a name = "Title" instead of "Movie.Title"
I do not wish to split my view into partial views or lose my strongly typed view by using ViewData or the like.
Is there a way to express to the View that I do not wish to have the Movie. prefix? I noticed that you can set:
ViewData.TemplateInfo.HtmlFieldPrefix = "x";
in the controller, but unfortunately it seems only to allow adding an additional prefix. Setting it to "" does nothing.
Is there any work around for this? Or am I stuck with the unfortunate prefix that isn't really necessary in this case if I wish to keep strongly typed views and lambdas?
Thanks for any help.
Update:
Here's the controller actions to maybe make things a bit clearer.
public ActionResult Edit(int? id)
{
var vm = new EditVM
{
Movie = id.HasValue ? _movieSvc.Find(id.Value) : new Movie(),
Genres = AppData.ListGenres()
};
return View(vm);
}
[HttpPost]
public void Edit([Bind(Prefix = "Movie")]Movie m)
{
_movieSvc.AddOrUpdateMovie(m); //Exceptions handled elsewhere
}
No, in order to do what you want you would have to rewrite the Html helpers, and then you would have to write your own model binder. Seems like a lot of work for minimal gain.
The only choice is a Partial view in which you pass the Movie object as the model. However, this would require you to write your own model binder to have it be recognized.
The reason you have to do m.Movie.Title is so that the ID has the correct name, so the model binder can recognize it as a member of your model.
Based on your update:
Your options are:
Use non-strongly typed helpers.
Use a partial view.
Rewrite the stronly typed helpers
Don't use the helpers at all, and write the values to the HTML
Personally, i'd just use 1 or 2, probably 2.
EDIT:
Based on your update above. Change your code to this (note, Genres does not get posted back to the server, so m.Genres will just be null on postback):
[HttpPost]
public void Edit(EditVM m)
{
_movieSvc.AddOrUpdateMovie(m.Movie); //Exceptions handled elsewhere
}
EDIT:
I did just think of an alternative to this. You could simply do this:
#{ var Movie = Model.Movie; }
#Html.TextBoxFor(m => Movie.Title)
However, if there was a validation error, you would have to recreate your EditVM.
I have a view model like this
I think that you might have some misunderstanding about what a view model is. A view model shouldn't contain any reference to your domain models which is what those Movie and Genre classes seem to be. I mean creating a new class that you suffix with VM and in which you stuff all your domain models as properties is not really a view model. A view model is a class that is specifically designed to meet the requirements of your view.
A much more correct view model would looks like this:
public class EditVM
{
public string MovieTitle { get; set; }
public IEnumerable<GenreViewModel> Genres { get; set; }
}
and in your view you would have:
#Html.EditorFor(x => x.MovieTitle)
#Html.EditorFor(x => x.Genres)
Another option is to either use the TextBox(string name, object value) overload instead of the TextBoxFor:
#Html.TextBox("Title", Model.Movie.Title)
You could also specify the input tag HTML instead of using a helper.
Another option is to take EditVM as your postback parameter. This is what I would do. My post action parameter is always the same type of the .cshtml model. Yes there will be properties like lists that are null, but you just ignore those. It also allows you to gracefully handle post errors as well because if there is an error you'll need to return an instance of that view model anyhow, and have the values they submitted included. I usually have private methods or DB layer that handles retrieving the various lists that go into the ViewModel, since those will be empty on postback and will need to be repopulated, while not touching the properties that were in the post.
With your post method as it is now, if you need to return the same view, you've gotta create a new EditVM and then copy any posted values into it, and still populate the lists. With my method, you eliminate one of those mapping steps. If you are posting more than one thing, are you going to have umpteen different parameters on your post action? Just let them all come naturally into a single parameter typed to the EditVM of the View. While maybe having those null properties in the VM during the postback feels icky, you get a nice predictable consistency between View and postback IMO. You don't have to spend alot of time thinking about what combination of parameters on your post method will get you all the pieces of data from the form.
I am using ASP.NET MVC. My requirement is to build a complex object (an object made of other object) through a step-by-step procedure like in a wizard.
Every dependent object shall be build on it's step and shall be validated in it's step. For example
public class ComplexObjectModel {
public Object1 MyObject1 { get; set; }
public Object2 MyObject1 { get; set; }
public Object3 MyObject1 { get; set; }
}
As there is no built-in facility for a wizard I have decided to create 3 model classes and 3 strong typed partial views binded to these models.
On every step of my pseudo wizard I validate the dependent model object and set the property of the complex object to its reference.
I was thinking to save the complex object inside the ViewData/TempData in the following way
In the controller action
[HttpPost]
public ActionResult MyAction1() {
ComplexObjectModel com = (ComplexObjectModel)ViewData["ComplexObjectModel"];
com.MyObject1 = new Object1();
ViewData["ComplexObjectModel"] = com;
return PartialView( "MyAction2", com.Object1 );
}
and in the View
<% using (Html.BeginForm()) { %>
<%= Html.Hidden("ComplexObjectModel", ViewData["ComplexObjectModel"]) %>
... view fields for Object1, Object n ....
<% } %>
But doing this way the object is not passed back-and-forth between the view and the controller and I have experienced that is null when it comes back from the view to the next action.
Is there a way to support this requirement?
thanks for helping
There are a couple of ways I might tackle this.
First; I might decide to store all this in the session object. I am assuming here that the models are quite large and so I wouldn't want them stored on the view and passed back each time I go to the next page.
Second; I might store them in the database and if the wizard didn't complete then delete them as part of a background process.
The one thing I wouldn't do is pass the complex object to each view. The view should really need to know anything about any other view in a restful world and so I'd be inclined not to do it.
Of course that does mean you need to decide a storage place for the data. If I had a Large objcect then I'd choose the database and if was fairly small then I'd choose the session object.
As you have already found, having all the data for each object in each view can be problematic.
However, if you are determined to do this the View way then here is what I'd do;
Create a partial view which deals
only with each object in the complex
model.
On each view, include all three, or
more, of the partial views.
For each partial view which is not
an active participant in the view,
place it within a div that is
hidden.
At least then when you change a property, or add one, you simply set it in the partial view once and not three times. Also if there is an error, you can unhide the divs and see if the data is coming in.
Also each field should then have the id of ModelName.Property so that the controller knows where the property is.
<%= Html.TextBox("MyObject1.MyProperty1", value) %>
Then in the controller you simply do, and this off the cuff;
[HttpPost]
public ActionResult MyAction1(ComplexObjectModel complexModel) {
You could take a look at MVC Futures Html.Serialize helper method which allows you to keep state into a hidden field between the controller actions in a similar way classic WebForms does it.
I have an ASP.NET MVC application which I want to dynamically pick the partial view and what data gets passed to it, while maintaining strong types.
So, in the main form, I want a class that has a view model that contains a generically typed property which should contain the data for the partial view's view model.
public class MainViewModel<T>
{
public T PartialViewsViewModel { get; set; }
}
In the User Control, I would like something like:
Inherits="System.Web.Mvc.ViewUserControl<MainViewModel<ParticularViewModel>>" %>
Though in my parent form, I must put
Inherits="System.Web.Mvc.ViewPage<MainViewModel<ParticularViewModel>>" %>
for it to work.
Is there a way to work around this? The use case is to make the user control pluggable. I understand that I could inherit a base class, but that would put me back to having something like a dictionary instead of a typed view model.
You can use the DisplayTemplates and EditorTemplates to accomplish this. So if I'm reading your question right, you have a setup like this:
If you are using .NET 4.0 (yay for covariant generics!)
System.Web.Mvc.ViewPage<MainViewModel<object>>
If you are using .NET 3.5:
System.Web.Mvc.ViewPage<MainViewModel<object>>
public class MainViewModel
{
public object PartialViewsViewModel { get; set; }
}
You can then invoke DisplayFor on that object to get a partial view. So invoking:
<%= Html.DisplayFor(m => m.PartialViewsViewModel) %>
Will look for a template in your DisplayTemplates folder for a skin of the name of your type. So if you have a ParticularViewModel.ascx in your DisplayTemplates, it will use that control as the 'partial view'. If you were using some other kind of view model type, then search for OtherViewModel.ascx (for example).
The template for ParticularViewModel.ascx would then have:
System.Web.Mvc.ViewUserControl<ParticularViewModel>
Which lets you treat the object as a strongly typed model.
I'm kind of new to ASP.NET MVC and to the MVC pattern in general but I'm really digging the concept and the rapidity with which I can compose an app. One thing that I'm struggling with is how to expose more than one object to a view. I use a lot of strongly typed views, which works well but what if my view relies on more than one object? Right now I'm doing something pretty hacky and passing a Dictionary to the view and then just keying the different objects. Is there a better pattern for this?
You have two primary options and either could work well depending on your application.
1) Just put objects into the ViewData collecion. This works well if you have lots of different controllers that pass different data to different views. It also depends on how much boxing/unboxing you want from object as you cast objects to their correct types. This option is more flexible, but less type-safe and possibly more fragile.
2) Create strongly-typed objects that contain other strongly-typed objects useful to a set of views. This works well if you tend to pass the same data to most of your views and you have fewer controllers.
You could also consider passing an object that exposes an interface that can acquire different model objects (kind of a locator class), but that probably causes more problems than it does fix them.
make a nested class in your controller.
public class WhateverControllerViewData
{
public ObjectA ObjectA {get;set;}
public ObjectB ObjectB {get;set;}
}
Assign to them in your ActionMethods
{
var wcvd = new WahteverControllerViewData;
wcvd.ObjectA = whatever;
..
Return View(wcvd);
}
Then use it in your View
<%= ViewData.Model.ObjectA.Whatever %>
Make sure you create a strongly typed view with your nested class as the type.
You can simply store each object in the ViewData then cast the appropriate object type in your View.
Controller:
ViewData["ObjectA"] = objectA;
ViewData["ObjectB"] = objectB;
View:
<%= ((ObjectA)ViewData["ObjectA"]).PropertyA %>
<%= ((ObjectB)ViewData["ObjectB")).PropertyB %>
or better yet,
<%
var objectA = (ObjectA)ViewData["ObjectA"];
var objectB = (ObjectB)ViewData["ObjectB"];
%>
<%= objectA.PropertyA %>
<%= objectB.PropertyB %>
I would architect your model to contain your various object types:
public class TheModel {
public DataClassA DataTypeA { get; set; }
public DataClassB DataTypeB { get; set; }
}
This eliminates casting and dictionary objects, etc.
Use the ViewData collection to pass object to the view. You might want to pass a Controller instead of 1 by 1 object of your model.
How about using anonymous types in ViewData.Model?
Tuple object can be used for that purposes as well,
Tuple <Type1, Type2> typleObject
But strongly typed container object is more preferred as far as you can define property names