RenderPartial, model as argument - c#

I get a type error but I don't understand why, I have a View, a ViewModel, a PartialView and a Model.
The view gift has giftViewModel as model.
giftViewModel contain an instance of the Model LoginModel (login).
The partialView LoginPopUp takes a LoginModel as model.
I try to render the partialView LoginPopUp within the gift view, passing it login as model.
And I get this error :
The model item passed into the dictionary is of type 'GiftViewModel', but this dictionary requires a model item of type 'LoginModel'.
Here is the code:
GiftViewModel.cs
public class GiftViewModel
{
public LoginModel login { get; set; }
[...]
}
Gift/Index.cshtml
#model GiftViewModel
#section content{
#{Html.RenderPartial("LoginPopUp", Model.login);}
}
LoginPupUp.cshtml
#model LoginModel
[...]
I really don't understand where I am wrong...

You should check whether Model.login != null in the line
#{Html.RenderPartial("LoginPopUp", Model.login);}
In case it is equal, the framework will pass model form the parent view to the LoginPopUp, which is type of GiftViewModel. That is why you are getting this error, because the partial view requires a model item of type LoginModel.
So either initialize the login property before that, say in controller, or do something like
#{Html.RenderPartial("LoginPopUp", Model.login ?? new LoginModel());}

Related

How do I pass a partial view with a specific model if another partial view is using a different model?

I am trying to view the cart on my page which is already using a different view model. Here is my Layout:
#RenderBody()
....
<div id="cd-shadow-layer"></div>
<div id="cd-cart">
#Html.Partial("_ItemsCartPartial", (Model as List<TShirtEmpAdmin.Models.Order>))
</div>
The view that is rendered in the body is using
#model TShirtEmpAdmin.ViewModels.ShirtOrdersViewModel
THe html partial is rendering
#model IEnumerable<TShirtEmpAdmin.Models.Order>
The model item passed into the dictionary is of type 'ViewModels.ShirtOrdersViewModel', but this dictionary requires a model item of type 'Models.Order'.
How do I fix this?
The error occurs because your view is using a model that is typeof ShirtOrdersViewModel and its layout is calling a partial that expects List<Order>. Because the layout has
#Html.Partial("_ItemsCartPartial", (Model as List<Order>))
you are attempting to cast the model which is ShirtOrdersViewModel to List<Order> which fails and the result of that is the same as
#Html.Partial("_ItemsCartPartial", null)
When you pass null as the model, the method uses the ViewDataDictionary of the main view and so the method now passes ShirtOrdersViewModel to a partial expecting List<Order>, hence the exception.
Your current implementation means that your layout can only be used by a view whose model is List<Order> (or is a model that derives from a BaseModel which contains a property List<Order> in which case you could use #Html.Partial("_ItemsCartPartial", Model.MyListOfOrdersProperty)). However that would be the wrong approach in your case, and instead you should create a method that returns a PartialView of the orders, for example
[ChildActionOnly]
public ActionResult Orders()
{
List<Order> orders = .... // get orders for the current user
return PartialView("_Orders", orders);
}
and the _Orders.cshtml file would have #model List<Order> and the code to display them. Then in the layout, render the results of the partial using
#{ Html.RenderAction("Orders", yourControllerName); }

does a partial view always need a model passed from the top-level view?

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)

Displaying partial page within a View page

I have a Controller/ShoppingCart which has a Action Method Index() and I created a Index View Page which is strongly type and uses a model class
Project.Models.ShoppingCartViewModel
I have an another Controller/Details which has an Action Method Confirm() and I created a Confirm View Page which is also a strongly type and uses a model class
Project.Models.Confirm
Now all I want to do is that Create a Partial View of Index() which will use a #model Project.Models.ShoppingCartViewModel and display this partial page on right side of Confirm View Page. Both are strongly typed view.
You should add a new property to Confirm view model of type ShoppingCartViewModel
public class Confirm
{
public ShoppingCartViewModel Cart {set;get;}
//Other Properties of your viewmodel goes here
}
Now in the Confirm view, Call the Html.Partial helper method to render the Partial view which displays shopping cart and pass the Model.Cart property.
#model Project.Models.Confirm
<h2>Confirm order</h2>
#Html.Partial("~/Views/ShoppingCart/_CartPartial.cshtml".Model.Cart)
Assuming your partial view is located at ~/Views/ShoppingCart/_CartPartial.cshtml location.
Make sure you properly initialize the Cart property to avoid Null Reference exception (Object reference not set to an instance of object)
public ActionResult Confirm()
{
var vm = new Confirm();
vm.Cart= new ShoppingCartViewModel();
// Load the Shopping cart property values to vm.Cart
return View(vm);
}

Passing a model property to a partial view

I'm having trouble passing the correct data to a partial view, and I'm not sure why it is failing.
Say I have models
public class SubModel {
public string Wobble {get; set;}
}
public class MyModel {
public SubModel Wibble {get; set;}
}
and views
MyView.cshtml
#model MyModel
#Html.Partial("SomePartial", Model.Wibble)
and
SomePartial.cshtml
#model SubModel
<h1>Victory!</h1>
this fails with The model item passed into the dictionary is of type 'MyModel', but this dictionary requires a model item of type 'MySubModel'
When I change MyView.cshtml to
#model MyModel
#Html.Partial("SomePartial", Model.Wibble, new ViewDataDictionary<MySubModel>(Model.Wibble))
It works as expected.
Why do I need to explicitly pass a ViewDataDictionary? Why can't I just pass the model?
Note: I'm using a library that does things that make me go hrm? a lot of the time. If the above behaviour is not expected, it might be this libraries fault.
This happens when the model you pass to the partial view is null. Don't pass null to a partial view, or it will get confused about its type.
Did you try passing the submodel with Model.Wibble instead of MyModel.Wibble? The associated model is accessible in a view with Model, not the model's name.

asp.net using a different model in a partial view than in its parent?

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);
}

Categories

Resources