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);
}
Related
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); }
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)
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 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());}
Lets say that i have an URL that looks something like this: localhost/userdetails/5 where 5 is the users ID. Is there any way to make use of the ID directly in the view (razor viewengine) and show the details? Or do i handle it in the default action in the controller?
To keep things simple now, focusing on getting the id to the view, you basically want to use the id to populate your model with data and then pass that to the view. So in your controller:
public ActionResult Index(int id)
{
UserModel model = db.Users.Where(u => u.Id == id).SingleOrDefault();
return View(model);
}
The view (very simplified) might look like this:
#model MyProject.Models.UserModel
#Html.DisplayFor(m => m.Id)
#Html.DisplayFor(m => m.Username)
This is very basic though. Eventually, you'll get to a point where you realise you should use viewmodels for your views instead of a domain model that's come directly from the data source. That in itself gives you another problem to solve in the form of mapping properties from the domain model onto your viewmodel. Typically, AutoMapper or ValueInjecter are used for that. For now though, it's probably best to just focus on passing data to a view and getting it back into a controller so that you can do something with it.
Update
This is a simple scenario which demonstrates how to get the data back into the controller. So basically, you'd have a form which you would submit:
#using (Html.BeginForm("Index", "Home"))
{
// Form elements and submit button
}
That would post the data to this action method for you to do whatever you wish with the data:
[HttpPost]
public ActionResult Index(UserModel inputModel)
{
// Check to see if the model's data was valid.
if (ModelState.IsValid)
{
// Do something in the database here.
// Then redirect to give the user some feedback.
return RedirectToAction("Thanks");
}
// The model validation failed so redisplay the view.
return View(inputModel);
}
you can use this in both the controller or in the View as an extension method.
Example: asuming your routes id holder has the default values in global.asax
public int IdFromAdress(HttpContext httpContext)
{
RouteData rd = httpContext.Request.RequestContext.RouteData;
string stringId = (string)rd.Values["id"];
return int.Parse(stringId);
{
You can get the id with this
#HttpContext.Current.Request.RequestContext.RouteData.Values["id"].ToString()
But I would reccomend to use a ViewMdoel to pass the value to the view and not the ViewBag or accessing directly from the view
You should use the model (i.e. the model passed back to your view). A ViewBag is another option but since the ID is part of the model itself, it wouldn't make any sense to do that.
View
#model User
#{
ViewBag.Title = "User Details";
}
#Model.Id;
Controller
public ActionResult UserDetails(int id)
{
return View("UserDetails", (object)id);
}
Yes you can. There is more than one way to do it, but since you've tagged your post MVC, assume you'll want to do it the 'MVC way', which means (imo) using a view model.
So you write a view model
public class MyViewModel()
{
public int ID {get; set;}
}
You populate the model in the controller and pass it to the view
public ActionResut MyView (int id)
{
var viewModel = new MyViewModel {ID = id};
return View (viewModel);
}
Then you have a strongly typed view (strongly typed to the MyViewModel, that is)
and you can reference the model's properties
#Model.ID
Then to make this useful, you can add whatever other properties you're wanting to work with to your view model. Then you can populate them in your controller before rendering the view (to show user info, for example), or let the user populate them for you in the view (using textboxes and such wrapped in a form). Then you can collect the user input in the post action in the controller like so
[HttpPost]
public ActionResult MyView(MyViewModel viewModel)
{
//do stuff with the data from the viewModel
}