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.
Related
Here's a url describing partial views in MVC:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial
Based on this url it looks like partial views are bound to a model that's passed to it from the partial view's top-level/parent view. Is this the standard and expected way to implement partial views?
This seems to indicate that a partial view intended to be used from several different parent views should have some type of associated specialized class that can be used to return its data to multiple different viewmodel builders. Is this the correct interpretation of the MVC partial view architecture?
Yes. By default it uses the parent views (view) model. But you can always pass another model to it explicitly ( as long as the type of the model passing is the same type which the view is strongly typed to).
#Html.Partial("MyPartialView",Model)
Or
#{ var data = new MyClass { SomeProperty = "SomeValue"};
#Html.Partial("MyPartialView",data )
Assuming MyPartialView is strongly typed to MyClass
#model MyClass
For example, If your main view is strongly typed to Order class which has a Customer property like this
public class Order
{
public int OrderId { set;get;}
public Customer Customer { set;get;}
}
public class Customer
{
public string Name { set;get;}
}
You can call the partial view which is strongly typed to the Customer class from your main view by passing the Model.Customer
#model Order
<h2>#Model.OrderId</h2>
#Html.Partial("Customer",Model.Customer)
Assuming your Customer view is strongly typed to Customer type
#model Customer
<h2>#Model.Name</h2>
You can call the Customer partial view from anywhere as long as you have a Customer object to pass to it. ( IF your parent view is strongly typed to Customer class, you do not need to explicitly pass it)
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.
My _Layout page uses #RenderSection at a couple of points, for example to render the sidebars (if there are any). I have about 30 different views that use 10 different models where they get the content from. However there are only 4 different sidebars at the moment, so I out them into partial views that are called like this:
#section SBLeft {
#Html.Partial("_SidebarTopics)
}
This works fine on my frontpage because the sidebar partial _SidebarTopics that is called from the Frontpage\Index.cshtml view uses the same model (WebsiteStructureModel) that is called at the start of the Index view:
#model Web.Areas.Public.Models.WebsiteStructureModel
Now I run into problems when I want to use a sidebar that uses Model A, if the "parent" view uses Model B. It results in an error like this:
The model item passed into the dictionary is of type 'Web.Areas.Public.Models.ProjectDetailsModel', but this dictionary requires a model item of type 'Web.Areas.Public.Models.WebsiteStructureModel'.
Using two #model statements at the beginning of the Index view does not work so I can't pass the second model explicitly to the sidebar as second parameter of the #Html.Partial command. And using a #model statement at the beginning of the partial view is ignored.
There must be some way to call a partial view and have that partial view use a specified model that may not neccessarily be the one used by the calling/parent view - Please help me understand how this can be done!
There are 2 ways to do this.
First way:
You could combine your 2 models into a view model:
public class ViewModel
{
public WebsiteStructureModel WebModel { get; set; }
public ProjectDetailsModel ProjectModel { get; set; }
}
You'd obviously populate this in your Action and then pass that to the View
Second way:
Rather than calling #Html.Partial("_SidebarTopics") you could create an Action in your controller which will return PartialView("_SidebarTopics", model); where model is the model passed into the partial view, for example:
#section SBLeft {
#Html.Action("SidebarTopics", new { /* route params */ });
}
Controller:
public ActionResult SidebarTopics(/* route params */)
{
var model = new ProjectDetailsModel();
return PartialView("_SiderbarTopics", model);
}
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 :-)
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.