I have a view that contains many partial views and I need to pass to each one the matching model.
I've found 2 ways to do this, but I don't know whats the real way it should be done.
I thought of creating big class that contain all the models as properties and than i can send the models to each partial view. the problem is that its hard typed, and if i need to pass a diffrent combination of models it wont fit.
The other way I though of is having a methods in each model that bring me the model for each partial view (GetMenuBar() and so on).
Whats the right way doing that?
My advice, go with Option 1. I use that with all of my Main View/Multiple Partial View scenarios. It's easy to maintain as each partial has it's own ViewModel. It keeps the whole thing nice and clean
I use the exact same setup like so:
public class MainViewModel {
public Partial1ViewModel Partial1 [ get; set; }
public Partial2ViewModel Partial2 [ get; set; }
public Partial3ViewModel Partial3 { get; set; }
public Partial4ViewModel Partial4 { get; set; }
public MainViewModel() {}
public MainViewModel() {
Partial1 = new Partial1ViewModel();
Partial2 = new Partial2ViewModel();
Partial3 = new Partial3ViewModel();
Partial4 = new Partial4ViewModel();
}
}
Each PartialViewXViewModel is it's own ViewModel and if need be can be reused in another view.
Your Action that renders can look like so:
public ActionResult Index {
var model = new MainViewModel();
return View(model);
}
Your View
#model MainViewModel
<div>
{#Html.RenderPartial("PartialOne", Model.Partial1)}
</div>
<div>
{#Html.RenderPartial("PartialTwo", Model.Partial2)}
</div>
<div>
{#Html.RenderPartial("PartialThree", Model.Partial3)}
</div>
<div>
{#Html.RenderPartial("PartialFour", Model.Partial4)}
</div>
Define the UI for each PartialX like:
#model Partial1ViewModel
//view html here
Now, each Partial view html and each model that they use can be used anywhere.
The great part is now if you have a page that needs only 2 of these you just create a new ViewModel to represent that specific view like so:
public class OtherMainViewModel {
public Partial2ViewModel Partial2 [ get; set; }
public Partial4ViewModel Partial4 { get; set; }
public OtherMainViewModel() {}
public OtherMainViewModel() {
Partial2 = new Partial2ViewModel();
Partial4 = new Partial4ViewModel();
}
}
And use it in another view like so:
public ActionResult SomeOtherAction {
var model = new OtherMainViewModel();
return View(model);
}
And that's perfectly acceptable and also the preferred design strategy in MVC, to have ViewModels that specifically represent what a view needs and only what it needs.
You may want to use a different method for populating your models tho. Most here would recommend using Automapper. Either way, the above just initializes the PartialViewXModels in the constructor of the MainViewModel. That won't necessarily be your case if you are populating those models with data from your DB. You would want your own strategy for that. This would work here:
public ActionResult Index {
var model = new MainViewModel();
model.Partial1 = GetPartial1Data(); // this method would return Partial1ViewModel instance
model.Partial2 = GetPartial2Data(); // same as above for Partial2ViewModel
...
return View(model);
}
This all would just get you started with the design, you can tweak it to your hearts content :-)
Related
I have a ViewModel that has a complex object as one of its members. The complex object has 4 properties (all strings). I'm trying to create a re-usable partial view where I can pass in the complex object and have it generate the html with html helpers for its properties. That's all working great. However, when I submit the form, the model binder isn't mapping the values back to the ViewModel's member so I don't get anything back on the server side. How can I read the values a user types into the html helpers for the complex object.
ViewModel
public class MyViewModel
{
public string SomeProperty { get; set; }
public MyComplexModel ComplexModel { get; set; }
}
MyComplexModel
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
....
}
Controller
public class MyController : Controller
{
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
model.ComplexModel = new MyComplexModel();
model.ComplexModel.id = 15;
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// model here never has my nested model populated in the partial view
return View(model);
}
}
View
#using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
....
#Html.Partial("MyPartialView", Model.ComplexModel)
}
Partial View
#model my.path.to.namespace.MyComplexModel
#Html.TextBoxFor(m => m.Name)
...
how can I bind this data on form submission so that the parent model contains the data entered on the web form from the partial view?
thanks
EDIT: I've figured out that I need to prepend "ComplexModel." to all of my control's names in the partial view (textboxes) so that it maps to the nested object, but I can't pass the ViewModel type to the partial view to get that extra layer because it needs to be generic to accept several ViewModel types. I could just rewrite the name attribute with javascript, but that seems overly ghetto to me. How else can I do this?
EDIT 2: I can statically set the name attribute with new { Name="ComplexModel.Name" } so I think I'm in business unless someone has a better method?
You can pass the prefix to the partial using
#Html.Partial("MyPartialView", Model.ComplexModel,
new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})
which will perpend the prefix to you controls name attribute so that <input name="Name" ../> will become <input name="ComplexModel.Name" ../> and correctly bind to typeof MyViewModel on post back
Edit
To make it a little easier, you can encapsulate this in a html helper
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ?
name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
}
};
return helper.Partial(partialViewName, model, viewData);
}
and use it as
#Html.PartialFor(m => m.ComplexModel, "MyPartialView")
If you use tag helpers, the partial tag helper accepts a for attribute, which does what you expect.
<partial name="MyPartialView" for="ComplexModel" />
Using the for attribute, rather than the typical model attribute, will cause all of the form fields within the partial to be named with the ComplexModel. prefix.
You can try passing the ViewModel to the partial.
#model my.path.to.namespace.MyViewModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
Edit
You can create a base model and push the complex model in there and pass the based model to the partial.
public class MyViewModel :BaseModel
{
public string SomeProperty { get; set; }
}
public class MyViewModel2 :BaseModel
{
public string SomeProperty2 { get; set; }
}
public class BaseModel
{
public MyComplexModel ComplexModel { get; set; }
}
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
...
}
Then your partial will be like below :
#model my.path.to.namespace.BaseModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
If this is not an acceptable solution, you may have to think in terms of overriding the model binder. You can read about that here.
I came across the same situation and with the help of such informative posts changed my partial code to have prefix on generated in input elements generated by partial view
I have used Html.partial helper giving partialview name and object of ModelType and an instance of ViewDataDictionary object with Html Field Prefix to constructor of Html.partial.
This results in GET request of "xyz url" of "Main view" and rendering partial view inside it with input elements generated with prefix e.g. earlier Name="Title" now becomes Name="MySubType.Title" in respective HTML element and same for rest of the form input elements.
The problem occurred when POST request is made to "xyz url", expecting the Form which is filled in gets saved in to my database. But the MVC Modelbinder didn't bind my POSTed model data with form values filled in and also ModelState is also lost. The model in viewdata was also coming to null.
Finally I tried to update model data in Posted form using TryUppdateModel method which takes model instance and html prefix which was passed earlier to partial view,and can see now model is bound with values and model state is also present.
Please let me know if this approach is fine or bit diversified!
Is there any way to pass list of models, each generated by HTML Partial View to a Controller Action in ASP.Net MVC 5?
So I have a View (with a model called WordModel) where I call a HTML partial View (with a model called MeaningModel) multiple times in a form like the following:
#using (Html.BeginForm("Create", "Entry", FormMethod.Post, new { id = "createForm" })) {
#Html.AntiForgeryToken()
<!--some other things here-->
#Html.Partial("_Meaning", new MeaningModel() { Number = 1 })
#Html.Partial("_Meaning", new MeaningModel() { Number = 2 })
#Html.Partial("_Meaning", new MeaningModel() { Number = 3 })
<!--some other things here-->
}
And the MeaningModel consists of multiple basic elements like the following:
public class MeaningModel {
public string MeaningValue { get; set; }
public int HomonimNumber { get; set; }
public string Example1 { get; set; }
public string Example2 { get; set; }
//and so on
}
Now, in my Controller, I have an Action which handles the Create form submission:
// POST: Meaning/Create
[HttpPost]
public ActionResult Create(WordModel model, List<MeaningModel> meanings, FormCollection collection) {
try {
// TODO: Add insert logic here
return RedirectToAction("Index");
} catch {
return View();
}
}
I could get the values I put in the main View in the WordModel model, but not the values in the Partial Views which consists of elements forming the List<MeaningModel> meanings. Is there any way to access them?
I could give each element in the MeaningModel different name per meaning model (such as MeaningValue_1, MeaningValue_2, MeaningValue_3, HomonimNumber_1, HomonimNumber_2, HomonimNumber_3, and so on) so that they will be captured by the FormCollection. But, as much as possible, I want to take advantage of the MeaningModel I have created and get them by the model.
Any way to do that?
I am using nested view models to display views based on user roles.
Model:
public class MainVM {
//some properties
public OneVM One {get; set;}
public TwoVM Two {get; set;}
}
public class OneVM {
//properties
}
public class TwoVM {
//properties
}
As written here that only main model is need to be sent controller. I am using Automapper to map properties from received model.
Controller:
public ActionResult EditAction(MainVM model){
var item = db.Table.Find(model.Id);
//automapper to map
AutoMapper.Mapper.Map(model.One, item); //does not work
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
}
Is this the right way to do that? What am I doing wrong here.
Update:
This was the view I was using to render nested view models from partial views
View:
#model MainVM
#Html.RenderPartial("_OnePartial", Model.One)
This answer https://stackoverflow.com/a/6292180/342095 defines an Html helper which will generate the partial view with right names.
The value of property One will be empty because you are passing an instance of OneVM to the partial (not the main model) so the form controls are not correctly named with the prefix (which need to be name="One.SomeProperty").
You have included a link to a PartialFor() helper (which works) but don't use it. In the main view it needs to be
#Html.PartialFor(m => m.One, "_OnePartial")
Which is the equivalent of
#Html.Partial("_OnePartial", Model.One,
new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "One" }})
The problem probably lies in your HTML. If a model is nested, then the input fields of properties should be like this:
<input type="text" name="SubModel.PropertyName" />
Using HTML helpers, it would look something like this:
#Html.EditorFor(model => model.SubModel.PropertyName)
The ASP.NET MVC Action cannot know, that you want to fill your submodel if it's not in your HTML.
I have two views: a partial view, and a view that encapsulates the partial view using #Html.RenderPartial("_PartialView"). Each has its own ViewModel:
public class PartialViewModel
{
// properties, etc.
}
public class MainViewModel
{
public PartialViewModel p { get; set; }
// properties, etc.
}
I'm getting dictionary errors when I load up the second view (the one that uses MainViewModel), because this view and the partial view it encapsulates are using two different ViewModels. I can't have them use the same ViewModel, because the partial view is rendered inside many other different views.
To be clear, both of these views contain forms, with the partial view representing all of the shared fields between forms. Given this, do I have any options, or am I simply attempting to do something that does not fit within the MVC design constraints?
you are going to want to design this a little differently . The main view will have a Model - lets call it MainModel, and the partial View can have a Model - we'll call it PartialModel
public class PartialModel
{
/// props
}
public class MainViewModel
{
public PartialModel Partial { get; set; }
// properties, etc.
// per comments in other answer you may want something like this
public MainViewModel()
{
Partial = new PartialModel();
}
}
then your main View will have
#model MainViewModel
then in the middle of the main view you have something like
#{ Html.RenderPartial("myPartialView", Model.Partial); }
Notice the braces around Html.RenderPartial. They're needed because RenderPartial return is void.
When you render the partial view you can also pass in the Model to it, if it is designed properly then the model that the partial needs will already be accessable from the main view's model
You can do this, but in your controller you're going to need to declare a new MainViewModel and assign to its PartialViewModel a new PartialViewModel.
Ex:
public class Controller {
public ActionResult Index(){
var mainViewModel = new MainViewModel { PartialViewModel = new PartialViewModel() };
return View(mainViewModel);
}
}
Now, I would delegate the creation of these models to a factory, but that's more advanced and not necessary until you get into refactoring.
I'm new to .Net development, and now are following NerdDinner tutorial. Just wondering if any of you would be able to tell me
What is the differences between ViewData
and ViewModel
(all I know is they are used to pass some form of data from controller to view) and perhaps tell me on what situation should I use ViewData instead of ViewModel and vice versa
Thanks in advance!
Sally
What is ViewData ?
dictionary object that you put data into, which then becomes
available to the view.
ViewData Sample
Controller Action method likes :
public class HomeController : Controller
{
public ActionResult Index()
{
var featuredProduct = new Product
{
Name = "Smart Phone",
QtyOnHand = 12
};
ViewData["FeaturedProduct"] = featuredProduct;
return View();
}
}
How to use ViewData on View ?
#{
var viewDataProduct = ViewData["FeaturedProduct"] as Product;
}
<div>
Today's Featured Product is!
<h3>#viewDataProduct.Name</h3>
</div>
What is a ViewModel ?
Allow you to shape multiple entities from one or more data models or
sources into a single object
Optimized for consumption and rendering by the view
Its like :
How to use ViewModel with MVC 3 ?
Domain Model
public class Product
{
public Product() { Id = Guid.NewGuid(); Created = DateTime.Now; }
public Guid Id { get; set; }
public string ProductName { get; set; }
}
ViewModel
public class ProductViewModel
{
public Guid VmId { get; set; }
[Required(ErrorMessage = "required")]
public string ProductName { get; set; }
}
Controller Action Method
[HttpGet]
public ActionResult AddProduct()
{
//for initialize viewmodel
var productViewModel = new ProductViewModel();
//assign values for viewmodel
productViewModel.ProductName = "Smart Phone";
//send viewmodel into UI (View)
return View("AddProduct", productViewModel);
}
View - AddProduct.cshtml
#model YourProject.ViewModels.ProductViewModel //set your viewmodel here
Conclusion
By using ViewModel can pass strongly-typed data into View
But ViewData is Loosely Typed.So Need to cast data on View
ViewModel can use for Complex scenarios such as merging more than one
domain model
But ViewData can be used only for simple scenarios like bring data
for the drop down list
ViewModel can use for attribute-based validation scenarios which
needed for Ui
But Cannot use ViewData for such kind of validations
As a best practices always try to use strongly typed data with
Views.ViewModel is the best candidate for that.
ViewData:
In short, use ViewData as support data, such as a datasource to a SelectList.
ViewModel:
ASP.NET MVC ViewModel Pattern
When a Controller class decides to render an HTML response back to a
client, it is responsible for
explicitly passing to the view
template all of the data needed to
render the response. View templates
should never perform any data
retrieval or application logic – and
should instead limit themselves to
only have rendering code that is
driven off of the model/data passed to
it by the controller.
[...]
When using [the "ViewModel"] pattern we create strongly-typed
classes that are optimized for our
specific view scenarios, and which
expose properties for the dynamic
values/content needed by our view
templates. Our controller classes can
then populate and pass these
view-optimized classes to our view
template to use. This enables
type-safety, compile-time checking,
and editor intellisense within view
templates.