ASP.NET MVC PartialView generic ModelView - asp.net-mvc

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.

Related

What is the right design pattern for custom template types in ASP.NET MVC?

Here's my situation: I've got a number of specialized object types in my application, and I'm following the standard convention for displaying them with custom templates in the /Shared/DisplayTemplates folder, and editing them with templates in /Shared/EditorTemplates. But I also want to be able to display a custom filter template for each type, so I'd like to add a /Shared/FilterTemplates folder, and implement my own #Html.FilterFor method, so that showing a Filter template is exactly like showing a Display or Editor template.
Does this seem like the best way to handle this situation, or is there a more correct/elegant way to do this in MVC? Thanks in advance.
I'm always using EditorTemplates when data is sent back to server. I assume the user can submit the filter to the server to perform the actual filtering.
When creating filters I prefer to create a model for the filter like:
public class UserListFilterModel
{
public string Username { get; set; }
public bool IsEnabled { get; set; }
}
The view for UserListFilterModel goes into EditorTemplates/UserListFilterModel.ascx.
And then add it as a property on my view model for the page.
public class MyPageViewModel
{
public UserListFilterModel Filter { get; set; }
}
Then I add the filter model to the model for the page and displays it like this:
<%= Html.EditorFor(x => x.Filter)%>
You are probably wrapping the filter in a form to allow the user to submit the values so I think it belongs in EditorTemplates. The users is in fact editing the filter model.
(If you really want to separate them ing you could use the UIHintAttribute but I wouldn't)
Edit: I added some sample code.
I think you misunderstand how Templates work. Templates do not make sense in the context you are describing.
Templates work on a SINGLE data item (although that data item can contain multiple data items, which in turn have their own templates).
The concept of a Filter is to control multiple data items, thus they do not map well to a template.
What you could do is create a DisplayTemplate for your collection class that adds filtering, thus no need to create a custom type of template. Just use DisplayTemplates.

Can you remove the HTML Field Prefix from strongly typed models in MVC 3?

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.

Alternatives to ViewData?

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.

Building a complex Object step by step. Where to save it?

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.

Reusable Content Box Data In ASP.NET MVC?

If I create a PartialView for a box that holds a header, image and content what is the best way to store the content without using a database?
Example: TurboTax
I doubt the content for the boxes in the sidebar are stored in a database but to make reusable code it would be beneficial to create the structure in a PartialView and populate the content areas. I can create a PartialView and pass a Model from the parent Controller to the PartialView but then I would be stuck copying and pasting that same content if I wanted to use the same box on another page.
For fixed content you might want to think about using XML+XSLT or even HTML snippets in the file system and simply rendering them. An HtmlHelper method might make more sense for this than a partial view - Html.RenderXml() or Html.Include(). The only real difference between these and partial views is that the view engine isn't invoked since there aren't any substitutions. I do this sort of thing with my privacy policy and terms and conditions. I'd certainly consider keeping these cached.
If these really are templates and you are just substituting content, then I think the partial view works well and I would consider putting the data in a database, again, maybe using caching if I found that performance suffered. You could use this in combination with the former -- say keep your images/xml in the file system and a pointer to them in the database so you know which ones to pick in the partial.
Passing data to partial view that is used in many places can be done in many ways:
Create base model class for all your models. In base class define PartialModel property which will be holding model for partial view (there may be many of them if use have many partial views). Now you can populate the PartialModel property in controller action, but to make code more reusable you can create your own Action Filter which will insert the partial view data just after the action method is executed (but before the model is passed to the view)
public class PartialViewModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
BaseViewModel model;
if (filterContext.Controller.ViewData.Model == null)
{
model = new BaseViewModel();
filterContext.Controller.ViewData.Model = model;
}
else
{
model = filterContext.Controller.ViewData.Model as BaseViewModel;
}
model.PartialModel = new PartialModel(...) // Partial model initialization
base.OnActionExecuted(filterContext);
}
}
Then you can use it like:
[PartialViewModel]
public ActionResult Index()
{
//...
}
Another option: you can create BaseController class for all your controllers and create PartialModel on base controller initialization. Then PartialModel can be stored in ViewData[] dictionary. Because using ViewData dictionary in views is bad, create extension method on HtmlHelper like:
public static PartialModel GetPartialModel(this HtmlHelper helper)
{
return helper.viewContext.ViewData["PartialModel"] as PartialModel
}
So you could obtaint the model this way:
<% Html.RenderPartial("MyPartial", Html.GetPartialModel()); %>

Resources