I have the following EF generated data model:
public partial class PrinterMapping
{
public string MTPrinterID { get; set; }
public string NTPrinterID { get; set; }
public string Active { get; set; }
}
I then have the following view model:
public class PrinterViewModel
{
public PrinterMapping PrinterMapping;
public Exceptions Exceptions;
public IEnumerable<PrinterMapping> Printers;
}
In my Index Action in HomeController I am passing my view model to the Index view.
private eFormsEntities db = new eFormsEntities();
public ActionResult Index()
{
PrinterViewModel printerModel = new PrinterViewModel();
printerModel.Printers = from pt in db.PrinterMapping select pt;
return View(printerModel);
}
My Index view is calling a partial view in the following manner towards the end (probably wrong):
#Html.Partial("~/Views/Home/GridView.cshtml")
My GridView.cshtml looks like:
#model AccessPrinterMapping.Models.PrinterViewModel
<h2> This is Where the Grid Will Show</h2>
#{
new WebGrid(#model.Printers, "");
}
#grid.GetHtml()
I learned about the WebGrid method from http://msdn.microsoft.com/en-us/magazine/hh288075.aspx.
My WebGrid line isn't happy at all since it doesn't recognize #model within that line.
How do I access the Printers in the view model that I passed in? Is this even possible?
Thanks very much to you all.
Theres two issues with your code.
First, you should explicitly pass your model in like this:
#Html.Partial("~/Views/Home/GridView.cshtml", Model) #* explicitly pass the model in *#
Then, because you are already in a code block in your partial view.. you don't need the # symbol.. and Model has an uppercase M.
new WebGrid(Model.Printers, "");
#model is a directive for your views/partial views. Think of it as a "configuration" command. Model is an actual property. It is the object that is passed into the view.. and is of the type you specified with the #model directive.
#{
new WebGrid(Model.Printers, "");
}
and also you have to pass your model into partial view in
#Html.Partial("~/Views/Home/GridView.cshtml")
in second parameter. I guess this call should be
#Html.Partial("~/Views/Home/GridView.cshtml", Model)
Related
I have a main model and a partial view. The model for the partial view is a new model with no values. But now I want to pass one element of parent model to the partial view. How do I go about it?
The parent and the partial views have different properties, so I cant use the same model.
Models:
public class Parent
{
.
.
public List<SelectListItem> TypeList { get; set; }
.
.
}
public class Partial1
{
public List<SelectListItem> TypeList1 { get; set; }
.
.
}
public class Partial2
{
public List<SelectListItem> TypeList2 { get; set; }
.
.
}
Parent View:
#model Models.Parent
#Html.Partial("_Partial1", new Partial1())
#Html.Partial("_Partial2", new Partial2())
I realize I can pass it to the partial view using a ViewDataDictionary and use it, but I was wondering if I could assign it to my partial view model directly. Can I do something like this?
#Html.Partial("_Partial1", new Partial1(new{TypeList1 =Model.TypeList}))
The above code gives a compile time error saying Partial1 does not contain a constructor that takes 1 argument.
EDIT 1:
According to Chris Pratt's answer I modified my code to
#Html.Partial("_Partial1", new Partial1{TypeList1 =Model.TypeList})
This solved the compilation error.But this is now causing a run time error in the partial View 1.
Partial1 View:
#model Models.Partial1
#Html.DropDownListFor(model => model.Type, Model.TypeList1, new { #class = "form-control" })
throws an error "There is no ViewData item of type 'IEnumerable' that has the key 'Type'."
Oh sorry. That code actually looked right on first glance, but now I see the issue. You're trying to pass the info as an anonymous object to the constructor, which is parameterless. Instead, you should be using the object initialization syntax:
new Partial1 { TypeList1 = Model.TypeList }
try using
#{
Html.RenderPartial("Partial1", new { param1 = model.property });
}
The MSDN Link.
As I found on 10 Good practices for asp.net mvc webapplications it is a good practice to split mvc models in ViewModel (Models representing the view) and InputModels (Representing data entered by user).
The ViewModel gets a property of Type InputModel. The InputModel carries the data that can be edited by the user.
public class EmployeeInputModel{
public string Name {get;set;}
public Id? DepartmentId{get;set;}
}
public class EmployeeCreateViewModel{
public IList<Department> Departments{get;set;}
public EmployeeInputModel EmployeeModel{ get;set;}
}
The Controller-methods look like:
public class EmployeeController : Controller{
public ActionResult Create(){
var viewModel = new EmployeeCreateViewModel(DepartmentService.GetAll(), new EmployeeInputModel());
return View(viewModel);
}
public ActionResult Create(EmployeeInputModel createModel){
try{
EmployeeService.Create(...);
return RedirectToAction("Index");
} catch (Exception ex){
var viewModel = new EmployeeCreateViewModel(DepartmentService.GetAll(), createModel);
return View(viewModel)
}
}
}
The View looks like:
#model EmployeeCreateViewModel
#Html.EditorFor(m => m.EmployeeModel)
The Editor Partial is just like:
#model EmployeeInputModel
#Html.TextBoxFor(m => m.Name)
#Html.DropDownFor(m => m.Department, ???)
This worked great for me, unless I came to the point of DropDownLists (in the sample see departments). Because the EditorTemplate doesnt know the ViewModel but just the InputModel.
I dont want to put the department list into the input model, because it is not the supposed place to be for this list (I would have to bind them). It has to be in the viewmodel. The properties of the input model should also not be in the ViewModel.
Does someone have any idea how to achieve a separation of viewmodel and input model in one view?
You would have to create a model abstraction of your departments. Something like this:
public class EmployeeInputModel{
public string Name {get;set;}
public List<DepartmentInputModel> Departments{get;set;}
}
public class DepartmentInputModel{
public int Id;
public string Name;
}
Then you can display just the name of the department in the drop down list. The value will then be the id of the department.
You can have a look at this SO question for a example.
You can use the ViewBag yo pass the list of departments to your partial.
Or use a partial view that accepts the view model as its model
I have this Index.cshtml class:
#model ProOptInteractive.ViewModels.InvoicePageViewModel
#{
ViewBag.Title = "Index";
}
<div>#Html.Partial("ListServices", Model.Services)</div>
<div>#Html.Partial("ListProducts", Model.Products)</div>
<div>#Html.Partial("Invoice", Model.InvoiceViewModel)</div>
And this is my InvoicePageViewModel:
namespace ProOptInteractive.ViewModels
{
public class InvoicePageViewModel
{
public InvoiceViewModel InvoiceViewModel { get; set; }
public IEnumerable<Service> Services { get; set; }
public IEnumerable<Product> Products { get; set; }
}
}
The issue is that whenever I load this view, I get an error saying that I have passed the wrong model to the dictionary, and it doesn't specify which one of the partial views is causing the problem. Each partial has a different model type, one IEnumerable called Product, another IEnumerable called Service, and the Invoice partial view having a viewmodel called InvoiceViewModel.
Can anybody explain how to go about making this work? I'm a bit noob at Razor and MVC by the way.
UPDATE
I get this error message:
The model item passed into the dictionary is of type 'ProOptInteractive.ViewModels.InvoiceViewModel', but this dictionary requires a model item of type 'ProOptInteractive.ViewModels.InvoicePageViewModel'.
The error is because you have set your Invoice partial view to have a model of type InvoicePageViewModel, yet you are passing it a model of type InvoiceViewModel.
Either update your InvoiceViewModel property to be of type InvoicePageViewModel, or change the Invoice view to use a model of type InvoiceViewModel.
Error is in line <div>#Html.Partial("Invoice", Model.InvoiceViewModel)</div>
Your view Invoice is accepting model of type
InvoicePageViewModel and you are passing InvoiceViewModel
Change your code to <div>#Html.Partial("Invoice", Model)</div>
or modify your Invoice view to accept InvoiceViewModel as
#model ProOptInteractive.ViewModels.InvoiceViewModel
Invoice.cshtml likely starts with:
#model ProOptInteractive.ViewModels.InvoicePageViewModel
replace it with:
#model ProOptInteractive.ViewModels.InvoiceViewModel
You can pass model to your partial view like this:
#Html.Partial("Invoice", Model.InvoiceViewModel)
or something similar.
Model for main view:
class InvoicePageViewModel {
...
public InvoiceViewModel InvoiceViewModel { get; set; }
public IEnumerable<Service> Services { get; set; }
public IEnumerable<Product> Products { get; set; }
...
}
Then update your partial view to accept view model like this:
#model InvoiceViewModel
...
I discovered the error. It was lying in the controller method. When I called the Index view (which corresponds to the ActionResult Index method) I was returning the viewmodel InvoiceViewModel to the Index page, even though it was strongly typed to InvoicePageViewModel. I changed it to this, which works:
public ActionResult Index()
{
var invoice = InvoiceLogic.GetInvoice(this.HttpContext);
// Set up our ViewModel
var pageViewModel = new InvoicePageViewModel
{
Products = proent.Products.ToList(),
Services = proent.Services.ToList(),
InvoiceViewModel = new InvoiceViewModel
{
InvoiceItems = invoice.GetInvoiceItems(),
Clients = proent.Clients.ToList(),
InvoiceTotal = invoice.GetTotal()
}
};
// Return the view
return View(pageViewModel);
}
Thanks to everyone for all the help and suggestions.
let's consider two views that use the same layout composed of:
A left column containing a "body" (which is filled differently by both views)
A right column that displays general information (passed via the model)
Instead of defining the right part twice, I wondered if I could create a PartialView to link directly from the layout page.
The problem is that the partial views implicitely inherit their models from the view that is being rendered. And since each view has its own model, I end up with a model type mismatch in the partial view.
From here I see two solutions:
I could insert the common part of the view model in the ViewBag. Unfortunately this means that each view that uses this layout has to implement this "convention" but nothing warns the developer about it at compile time...
I could use polymorphism to make each view model inherit from the same base class (edit: or interface) that the Partial Views uses. This would work up to a certain extend but would potentially exponentially increase in complexity as soon as I have a second partial view in the same layout.
So here are the questions:
Am I right with the assumptions above?
Do you see any other possibility?
Any return on experience on this?
Thanks a lot,
TB.
Use an Interface and implement it on the two models, this is exactly the kind of thing they're used for.
Here is an example of two different Views using two different Models that both implement an interface. This is subtyping instead of ad-hoc polymorphism.
public class ViewModelOne : IReusableView
{
public string Name { get; set; }
public string Something { get; set; }
public int ANumber { get; set; }
}
public class ViewModelTwo : IReusableView
{
public string Name { get; set; }
public string Thing { get; set; }
public string SomethingElse { get; set; }
public int ANumber2 { get; set; }
}
public interface IReusableView
{
string Name { get; }
}
So we have the really simple partial view here that is 'InnerPartialView':
#model TestIntegration.Models.IReusableView
<div>
#Model.Name
</div>
Which is used in the home and about pages of this example controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View(new ViewModelOne() { Name = "hello", Something="sdfsdfs", ANumber = 1 });
}
public ActionResult About()
{
return View(new ViewModelTwo() { Name = "hello 2", SomethingElse = "aaaddd", ANumber2 = 10, Thing="rand" });
}
}
The home view:
#model TestIntegration.Models.ViewModelOne
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
<p>
To learn more about ASP.NET MVC visit http://asp.net/mvc.
#Html.Partial("InnerPartialView")
</p>
The about view:
#model TestIntegration.Models.ViewModelTwo
#{
ViewBag.Title = "About Us";
}
<h2>About</h2>
<p>
Put content here.
#Html.Partial("InnerPartialView")
</p>
When you render the partial view, you can send it a model:
#Html.RenderPartial(MVC.Partials.Views.Sidebar, Model.SideBarModel);
So you could send down data as part of the parent model that is the model for the partial sidebar.
In partial views, models are of type dynamic so you don't need to know what type they are. However, you just need to make sure the model has the property you need. In other words you can use Model.MyProperty or Model.MyProperty as MyPropertyType when using Html.Partial.
I am getting this error when trying to pass my object to the view. I am new to MVC so please forgive me.
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[<>f__AnonymousType13[System.Int32,System.String,System.Nullable1[System.DateTime]]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[MvcApplication1.Models.storageProperty]'
I am trying to pass a list for a table that will show objects from the storageProperty table with the last date (if there is one) from the expenses table. Most properties have had at least one expense audit, some have had many, and others have had none.
Here is the code from the controller:
var viewModel = db.storageProperties.Select(s => new
{
s.storagePropertyId,
s.BuildName,
latestExpenseSurvey = (DateTime?)s.expenses.Max(e => e.expenseDate)
}).ToList();
return View(viewModel);
}
and the #model statement in the view calls for a storageproperty object. I am using mvc3 with the entity framework. It appears obvious that I cannot pass this list object in place of the storageproperty object, but I can't figure out what to do instead, how should I do this?
Thanks in advance.
Never pass anonymous objects to views. You should always pass view models.
So as always in an ASP.NET MVC application you start by defining a view model which will reflect the requirements of your view:
public class MyViewModel
{
public int StoragePropertyId { get; set; }
public string BuildName { get; set; }
public DateTime? latestExpenseSurvey { get; set; }
}
Then in your controller return an IEnumerable<MyViewModel>:
public ActionResult Index()
{
var viewModel = db.storageProperties.Select(s => new MyViewModel
{
StoragePropertyId = s.storagePropertyId,
BuildName = s.BuildName,
LatestExpenseSurvey = (DateTime?)s.expenses.Max(e => e.expenseDate)
}).ToList();
return View(viewModel);
}
and finally strongly type your view to a collection of this view model:
#model IEnumerable<MyViewModel>
<div>
#Html.EditorForModel()
</div>
Your Linq query projects to an anonymous type. You need to create a named type for this projection in order to refer to it from the view.