Partial View loading wrong model - c#

I have a partial view in which I have a model specified as such:
#model IEnumerable<AbstractThinking2015.Models.BlogModel>
#foreach (var item in Model.Take(5))
{
<li>#Html.ActionLink(item.Title, "Details", new { id = item.BlogModelId })</li>
}
And I'm calling this using:
<div class="widget">
<h4>Recent Posts</h4>
<ul>
#Html.Partial("View")
</ul>
</div>
But I'm getting the error:
The model item passed into the dictionary is of type
'AbstractThinking2015.Models.BlogModel', but this dictionary requires
a model item of type
'System.Collections.Generic.IEnumerable`1[AbstractThinking2015.Models.BlogModel]'.
I'm sure this is because the model being passed into the view is the single blog but I want to use the list that's defined in the partial view. Is there a way to do this?

If you do not pass a model t the partial explicitly, It will take the model of the parent view. So from your error message, It is clear that you are passing a single object of the BlogModel to your view from your action method, hence getting the error.
You need to make sure that your main view (where you are calling the partial), is also strongly typed to a collection of BlogModel objects.
public ActionResult Index()
{
var blogList=new List<Models.BlogModel>();
// or you may read from db and load the blogList variable
return View(blogList);
}
And the Index view, where you are calling the Partial will be strongly typed to a collection of BlogModel.
#model List<Models.BlogModel>
<h1>Index page which will call Partial</h1>
<div class="widget">
<h4>Recent Posts</h4>
<ul>
#Html.Partial("View")
</ul>
</div>

Related

PartialView with a ViewModel on _Layout.cshtml

I have a layout page which has a partial view. The partial view needs to loop through a property on the view model to show a list of categories. When a category is displayed I need to show a list of documents in that category. /Home/Index works, but when I try to view /Documents/Category/{id}, I get an error:
Additional information: The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[ViewModels.DocumentViewModel]', but this dictionary requires a model item of type 'ViewModels.HomeViewModel'.
_Layout.cshtml
...
<body>
#Html.Partial("_CategoryViewModel")
<div class="content">
#RenderBody()
</div>
HomeViewModel.cs
public class HomeViewModel {
...
public ICollection<DocumentCategory> Categories { get; set; }
public ICollection<Documents> Documents { get; set; }
...
}
_CategoryViewModel.cshtml (this should show a list of all categories)
#model ViewModels.HomeViewModel
...
#foreach (DocumentCategory item in Model.Categories)
{
<li>
<a href="#Url.Action("Category", "Documents", new { #id = #item.CategoryId })" title="View documents in the #item.Name category">
<span class="fa fa-files-o"></span> #item.Name
</a>
</li>
}
DocumentsController.cs
public ActionResult Category(int id)
{
var thisCategory = _ctx.Categories.Get(c => c.CategoryId == id).FirstOrDefault();
IEnumerable<DocumentViewModel> docs = null;
if(thisCategory == null)
{
TempData.Add("errorMessage", "Invalid category");
} else {
docs = thisCategory.Documents.ToList();
}
return View("Category", docs);
}
What's happening kind of makes sense - the PartialView on the Layout page needs to enumerate over a collection which isn't present in the ViewModel I'm using. I have no idea how to achieve this - the only way would seem to be to add a Categories property to every ViewModel in my site.
By default, using #Html.Partial() will pass the current model to the partial view, and because your Category.cshtml view uses #model List<DocumentViewModel>, then List<DocumentViewModel> is passed to a partial that expects HomeViewModel.
If you want to render a partial view for HomeViewModel on every page, then use #Html.Action() to call a ChildActionOnly method that returns the partial
[ChildActionOnly]
public ActionResult Categories
{
var model = new HomeViewModel()
{
.... // initialize properties
}
return PartialView("_CategoryViewModel", model)
}
and in the layout
#Html.Action("Categories", yourControllerName)
// or
#{ Html.RenderAction("Categories", yourControllerName); }
As I see it you have a few different alternatives.
1. Use Html.Action and create an Action that returns your view.
#Html.Action("Index", "Category") // Or your controller name.
I believe that there are some performance draw-backs with this approach because the whole MVC lifecycle will run again in order to render the result of the action. But then you can render the result of an action without having the correct model in the view that called it.
One may also argue that this breaks the MVC pattern, but it might be worth it.
2. Use a generic model (or an interface) in your _Layout.cshtml, and let your viewmodels inherit from that model.
In your _Layout.cshtml:
#model IBaseViewModel
And let all your viewmodels implement this interface.
public interface IBaseViewModel
{
ICollection<DocumentCategory> Categories { get; set; }
}
public interface IBaseViewModel<T> : IBaseViewModel
{
T ViewModel {get; set;}
}
Since you're placing #Html.Partial("_CategoryViewModel") in _Layout.cshtml I assume that it should be visible in all pages, so I think it's logical that all the controllers that are using _Layout.cshtml make sure that it gets the information it needs, and thus adding Categories to the model.
I use this approach all the time for stuff like breadcrumbs and menu-information (stuff that is used in all pages). Then I have a basecontroller that makes sure that Categories is populated with the correct info.

ASP Net MVC - Send different model on POST

is it possible to send an object from a strongly typed view to the Controller via Http-POST that does not equal the type of the original model.
For example:
I have a ViewModel like this:
public class PersonsViewModel
{
List<PersonViewModel> persons { get; set; }
PersonsViewModel() { }
}
public class PersonViewModel
{
//some properties
Person() { }
}
Now i have this View:
#model PersonsViewModel
<div>
#for(int i = 0; i > Model.persons.Count; i++)
{
#Html.EditorFor(Model.persons[i])
}
</div>
The editor could look like this:
#model PersonViewModel
<div>
#using (Html.Beginform("Postaction","Controller", FormMethod.Post)){
<div>
<!-- properties and textboxes here + submit button -->
</div>
}
<div>
The controller action
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult Postaction(PersonViewModel model)
{
//do something
}
}
This doesn't work because it seems the Controller is expecting a PersonsViewModel object. My workaround so far is to make a "big" Form that contains all PersonViewModel and send the complete PersonsViewModel to the controller.
Is it somehow possible to pass only one PersonViewModel to the Controller although the view is strongly typed?
Kind regards,
Martin
It could be done:
When used with collections Html.EditorFor is smart enough to generate input names that contain index so ModelBinder could successfully create a model as a collection of objects. In your case since you want to have a separate form per PersonViewModel object, you could create a partial view as a template for editing PersonViewModel and use Html.RenderPartial helper:
Assuming you have _PersonViewModel.cshtml partial view
#for(int i = 0; i > Model.persons.Count; i++)
{
Html.RenderPartial("_PersonViewModel", Model.persons[i]);
}
in the _PersonViewModel.cshtml you can not use neither one of editor helpers such as Html.EditorFor, Html.TextboxFor because they are going to generate identical ids for the same properties so you will have to manually create html inputs:
#model PersonViewModel
<div>
#using (Html.Beginform("Postaction","Controller", FormMethod.Post)){
<div>
#*Nottice the usage of Html.NameFor(m=>m.FirstName) for generating a name property value *#
<input type="text" name="#Html.NameFor(m=>m.FirstName)" value="#Model.FirstName">
</div>
}
<div>
This way you can post a single PersonViewModel object to the controller action

How to get partial view to asynchronously get data from controller. Perceived Performance application

Update:
I have a page that loads an html view as follows:
<div class="center-box">
<div id="ourTeam">
<div class="arrowLeft"></div>
<div class="arrowRight"></div>
</div>
<div class="containerParent">
#Html.Partial("Team", Model.TeamMembers == null ? new List<UsersViewModel>() : Model.TeamMembers)
</div>
</div>
The model passed into this view is PagesViewModel which includes a bunch of display variables and a few objects. It also includes this:
public IEnumerable<UsersViewModel> TeamMembers { get; set; }
Which is what is sent into the Partial View. The controller for the partial view looks as follows:
public ActionResult Team()
{
getInfo getting = new getInfo();
IEnumerable<UsersViewModel> getUsers = getting.getDisplayUsers();
return PartialView("Team", getUsers);
}
But when I run it the code never gets here. It just displays the empty List I sent into from the View.
The name of the partial view file is: Team.cshtml
The goal is to get the data from the controller for the partial view
This happens when Partial View Model passed from the View is null, you can handle it like this:
<div class="containerParent">
#Html.Partial("Team", Model.TeamMembers ==null ? new List<UsersViewModel>() : Model.TeamMembers)
</div>
or make sure that at least passed Collection is at least instantiated via constructor of ViewModel
if you are trying to render partial view via controller, then you have to use #Html.Action():
#Html.Action("actionname","controllername")

How to handle PartialView with different model than parent View

I'm using VS2012 RC with MVC4, bot for all intents and purposes let's pretend it's MVC3. I would like to know what the standard best practice(s) is on how to handle PartialViews with a form that uses a different model than the parent View.
For example, here is a view that displays a table of all the available Roles and also has a form that allows the user to add more roles.
Main View - Roles.cshtml:
#model IEnumerable<RobotDog.Models.RoleModel>
<table>
#foreach(var role in Model) {
<tr>
<td class="roleRow">#role.Role</td>
</tr>
}
</table>
<div class="modal hide">
#Html.Partial("_AddRolePartial")
</div>
_AddRolePartial.cshtml
#model RobotDog.Models.RoleModel
#using(Html.BeginForm("AddRole","Admin", FormMethod.Post)) {
#Html.TextBoxFor(x => x.Role, new { #class = "input-xlarge", #placeholder = "Role"})
<input type="submit" value="Submit" class="btn btn-primary btn-large"/>
}
Model:
public class RoleModel {
[Required]
[DataType(DataType.Text)]
[Display(Name = "Role")]
public string Role { get; set; }
}
Controller for View:
public ActionResult Roles() {
var model = from r in System.Web.Security.Roles.GetAllRoles()
select new RoleModel {Role = r};
return View(model);
}
Controller for PartialView:
[HttpPost]
public ActionResult AddRole(RoleModel model) {
try {
System.Web.Security.Roles.CreateRole(model.Role);
RedirectToAction("Roles");
} catch(Exception) {
ModelState.AddModelError("", "Role creation unsuccessful.");
}
return ????; // not sure how to pass ModelState back to partialView
}
I thought about creating a ViewModel that held RoleModel and IEnumerable<RoleModel> but it seems like there would be a more stream lined way to accomplish what I wanted without having to create a ViewModel everytime I wanted to use this PartialView.
I think you are asking how to pass a RoleModel to the add RoleModel modal popup. Since you are creating a new Role, I am assuming you are needing an empty model. You can either pass it in like below:
<div class="modal hide">
#Html.Partial("_AddRolePartial", new RoleModel())
</div>
Or just do a #Html.RenderAction("AddRole") with the supporing GET method of the controller to support populating the item.
public ActionResult AddRole() {
var model = new RoleModel();
//populate with any items needed for the Add Role Model View
return View(model);
}
I personally don't like using Partial views with forms, because Partial Views do not render submodels correctly (ie, they don't take into account the hierarchy of the model).
This is why Display and EditorTemplates exist. They work well for rendering specific data types.
However, in your case, since your view doesn't have any forms of its own, and the end result is just a single item of the collection of your parent model, then a Partial View is actually a better approach simply because you CAN pass a different model to it than the views uses.
As others have pointed out, you can easily pass an empty model to the partial as the second parameter. I don't like newing up new objects in the view, but it doesn't look like there's a lot of choice there, as the alternatives would be pretty messy.
How about the form post is changed to an ajax form post with a target update partial id being the div which you will add to the parent view (effectively surrounding Roles.cshtml).
Add a new action public ActionResult _Roles() which will return PartialView("Roles", model)
Next, in the Post Action, Return RedirectToAction(...Roles Partial Action ...) at the end and remove RedirectToAction("Roles") in the try.

ASP.NET MVC passing several data instances from controller to view

I'm trying to build an ASP.NET MVC 2 application. I want to pass data to a view from a controller. I am able to pass it when it is only a single data structure. In the controller:
private ArticlesDBEntities _db = new ArticlesDBEntities();
public ActionResult Articles()
{
return View(_db.ArticleSet.ToList());
}
and in the view, I iterated over the list like so:
<div id="demo1">
<% foreach (var item in Model) { %>
<ul>
<li id="<%= Html.Encode(item.Id) %>">
<%= Html.Encode(item.Title) %>
<ul>
<li id="phtml_2">
Child node 1
</li>
<li id="phtml_3">
Child node 2
</li>
</ul>
</li>
</ul>
<% } %>
</div>
(the child nodes are for testing reasons right now, don't have a real role)
However, I now want to handle a scenario when a user tries to access Home/Articles/Id, and not only pass the article list (used for populating a jsTree), but also the Article itself, so I can show it as well. However, when I tried creating a ViewData object like so:
public ActionResult Articles()
{
ViewData["articlesList"] = _db.ArticleSet.ToList();
return View();
}
I was unable to find out how to iterate over it in the view.
as far as passing multiple data items is concerned u can do it using view models (preferred way) or by viewdata. if u want to pass it through View model u can do something like
public class ArticlVM
{
public Article Myarticle{get;set;}
public IEnumerable<ArticleSet> Artcileset{get; set;}
}
u can populate this view model and pass it to view
in view u can access it like
<%=Model.Article.articlName%>
<%=Model.Article.articlDescription%>
<%foreach(var m in Model.Articleset){%>
<label><%=m.Property1%></label>
<%}%>
to iterate over ViewData["key"] u have to cast it to corresponding object like
<%=foreach(var m in (ArticleSet)ViewData["Articles"]){%>
<label><%=m.Property1%></label>
<%}%>
In your View you should be able to do
<% foreach(var item in ViewData["articlesList"]) %>
An alternative approach would be to create a new view model class that would hold both the article and the list:
class ArticleViewModel {
public Article Article { get;set;}
public IEnumerable<Article> Articles { get;set;}
}
and pass that into your view. Then you can access it through the Model property and you will get strong typing.

Categories