Partial Views with view models different to the main view - asp.net

My home-screen has a view model of DashboardViewModel. It has PartialViews with their own ViewModels such as CustomerSearchViewModel and SelectProductViewModel.
All three ViewModels are separate.
When I run the application I get this error:
The model item passed into the dictionary is of type
'Invoice.Web.ViewModels.DashboardViewModel', but this dictionary
requires a model item of type
'Invoice.Web.ViewModels.SearchCustomerWindowVM'.
I wonder what should I do to resolve this issue.
As planned, the Home screen will eventually integrate a lot of PartialViews with their own view models. Do I declare the Partial-view-models inside the DashboardViewModel? or do I simply have a single big DashboardViewModel for all partialViews to come?

You can have your partial view viewmodels as properties of your main viewmodel and call Html.Partial and pass these properties.
public class DashBoardVM
{
public string Name { set;get;}
public CustomerSearchVM CustomerSearch { set; get;}
public DashBoardVM()
{
CustomerSearch =new CustomerSerachVM();
}
}
In your dashboard view,
#model DashBoardVM
<h2>#Model.Name</h2>
#Html.Partial("CustomerSearch",Model.CustomerSearch)
Assuming CustomerSearch partial view is strongly typed o CustomerSearchVM class.

Another option is to use Html.Action() or Html.RenderAction(). This allows you to call a completely separate controller from your parent view, and return a completely different, non associated model. Here is a pretty good explanation on both rendering Partial Views and Actions. http://www.midnight-coding.com/2013/01/partial-vs-action-vs-renderpartial-vs-renderaction.html

Related

How do I use two models on a shared layout page in MVC 3 ASP.NET?

I have a main layout page that has a basic table setup. I have a Main Content cell that I use to put RenderBody() in. I have product listings that use a Products database and I have a menu on the right side that needs to access the User database. How can I list my products using the products model in the Main Content cell and use the users model to list info in my Menu cell?
The only method I have used (however I'm not entirely confident on it's best practice status) is to create a model for the page that includes both (in your case) the product model and the user model. Populate these models in the controller, then access the inner models as you normally would in the view.
You can use partials to render content sections.
Imagine your page uses this model:
public class PageModel {
public string Title { get; set; }
public ProductListModel Products { get; set; }
}
Then you can render your partial view and pass in the model for that:
#Html.Partial( "name of partial view", Model.Products )
A slightly more performance costly approach is to call an action to render the partial view:
public class ProductsController {
public ActionResult List() {
var model = new ProductListModel();
return View( "your partial view", model );
}
}
And in your view:
#Html.Action( "list", "products" )
The advantage of the latter approach is that you keep your page model relatively clean, so that it doesn't have to contain all the data needed to display the page.

ASP.NET MVC 2: Using duplicate input partial views without duplicate validation

In an application being rewritten, originally a certain form included a drop down where the user chose one of two major options for how the input would be used in calculations elsewhere.
The requirements now dictate that instead of this drop down, the interface should feature two otherwise identical forms appearing on the same page, one above the other. A parameter or hidden value is to determine whether the aforementioned option is set on each of the forms so that there is one form for each of the two options. This sets a boolean value on the model.
I'm using the same partial view for both appearances of the form, defining their differences during initialization. However, I'm still having one particular issue--if there is a validation error on one form, it appears on both. What's the best way to prevent this?
Am I just going to have to give in and make near-duplicate partial views, or is there a way to keep using the same one?
You could try something like this:
Create a base model for the form. That base model will have the properties and validation attributes that are common to its two child models:
public class BaseModel {
[Required]
public string Name { get; set; }
}
public class Model1 : BaseModel {
public bool Form1 { get; set; }
}
public class Model2 : BaseModel {
public bool Form2 { get; set; }
}
You can then create two different controller actions that accept those models as parameters:
public ActionResult PostForm1(Model1 model) { }
public ActionResult PostForm2(Model2 model) { }
And your partial view would have to add an input depending on which form it is:
<form action="<%=(isForm1 ? "/PostForm1" : "/PostForm2")>%">
<input type="hidden"
id="<%=(isForm1 ? "Form1" : "Form2")%>"
name="<%=(isForm1 ? "Form1" : "Form2")%>"
value="true" />
</form>
When a form gets posted, it should only do validation on its model and leave the other model untouched (you'd need a view model that has both form models as properties).
I'm not sure if any of this would work, but, again, its something you could try.

How can I run code from my layout file?

I used the following tutorial to help me build an RSS Reader in my ASP.NET MVC3 Razor application:
http://weblogs.asp.net/jalpeshpvadgama/archive/2011/08/17/creating-basic-rss-reader-in-asp-net-mvc-3.aspx
However, unlike the tutorial example, I want the RSS feed to be displayed on every page, and have therefore added it to my layout file, /Views/Shared/_Layout.cshtml
I currently only have 2 views on my site, and to get the RSS Reader to work on both views I've got the following code in my HomeController:
public class HomeController : Controller
{
//
// GET: /Index/
public ActionResult Index()
{
return View(CT.Models.RssReader.GetRssFeed());
}
public ActionResult About()
{
return View(CT.Models.RssReader.GetRssFeed());
}
}
From my WebForms experience, I would simply add the RSS Reader code in my master page code behind, and it would automatically work on every page.
Is there a Controller for layout pages which allows me to do the same?
How can I get this to work on every call of the layout page, without having to return anything?
EDIT: Following #Sebastian's advice, I've now added this code to a Partial View, removed CT.Models.RssReader.GetRssFeed() from return View() and included this in my layout file:
#Html.Partial("_MyPartialView")
The code in this partial view is:
<ul>
#foreach (var item in Model)
{
<li>
#item.Title
</li>
}
</ul>
However, I'm not getting a runtime error:
Object reference not set to an instance of an object.
It's erroring on the line #foreach (var item in Model)
You have to create a partial view and add functionality there.
Then in your layout, render this partial.
EDIT
Is your partial view really a partial view? The reason I said that is because you have "_" in front of the name which suggests that it might be a layout (might just be a naming convention).
To fix object reference error, you have to add the #Model declaration on top of your partial view.
Hope it helps.
UPDATE
In order to use different model in partial view, you need to explicitly declare which model you are going to use on render partialmethod.
#{Html.RenderPartial("../YourFeed", Model.YourFeedModel);}
Let me know if that resolved your issue.
The new error you are having is due to you not passing a Model to the partial view. You can do this with the second argument of the Html.Partial function...
Html.Partial("ViewName", MyModel);
As I think you are trying to do this in a Layout page you could also consider using a static reference to get your RSS feed. So forget about needing to pass in a Model and in your partial have:
#foreach (var item in RssRepository.GetFeed())
{
<li>
#item.Title
</li>
}
this like to a class something like...
public static RssRepository
{
public static MyModel GetFeed()
{
return new MyModel();//<- return what you would normally pass as a Model for RSS feeds
}
}
Hope that all makes sense

Strongly typed form in MVC which maps to a different type?

When I specify form inputs with the #Html.TextBoxFor method, then the generated input element would not necessarily map to the type that is expected in the form's action method.
Let's say I have two classes:
public class HomeA
{
public int A { get; set; }
}
public class HomeB
{
public int B { get; set; }
}
HomeA is the model of my view. If a controller action expects HomeB, then I can't provide the necessary input element in a strongly typed manner in my form:
#using (Html.BeginForm())
{
#Html.TextBoxFor(model => model.A)
}
This form will obviously not map to HomeB's property.
The controller action should not expect HomeB.
Use one view model per action.
If you are sending a ViewModel of XYZ, then in general your ActionMethod takes a ViewModel of XYZ.
Thats my general thoughts anyways for consistency/readability.
However if it works for you, do it as long as the relation is there.
ASP.net MVC - One ViewModel per View or per Action?
As for the note on composition vs. inheritance check out
ASP.NET MVC Architecture : ViewModel by composition, inheritance or duplication?
Check out
http://lostechies.com/jimmybogard/2009/04/24/how-we-do-mvc/
You would create a HomeAB class that contains both a HomeA and HomeB
If you have to create and to show some items which belong to both classes A & B, you can design an interface and then inherit that interface. Or you can create another class AB which inherits from A & B.
Hope this helps!

HTML.Partial - MVC 3 Razor

I have problem with returning a partial view from a controller with different model than my main View. For example:
public ActionResult Index()
{
//myModel - get Some Types
return View(mymodel);
}
public PartialViewResult Categories()
{
//my another Model - get different Types
return PartialView(myanothermodel);
}
And then in Index View:
#Html.RenderPartial("Categories")
I get an exception saying that it is of the wrong type. It expects first type(mymodel) instead of second type.
Is it possible to return different types for view and its partial view?
Thanks for response.
It looks like you're trying to render the action, not the view.
Call #Html.Action("Categories").
When you are using Partial View only use
#Html.Partial("Categories", Model)
or a especific Model with your own data
#Html.Partial("Categories", Model.Category)
I just understood a bit how partial view works. In your and my case actually, no need to define the Categories() action if you think the logic to get myanothermodel can be done in Index() action.
So I have mymodel.myanothermodel assigned in the Index() action and then in the strongly typed Index.cshtml I used this: (assume myanothermodel is Categories)
#{Html.RenderPartial("Categories", Model.Categories);}
alternatively:
#Html.Partial("Categories", Model.Categories)
Note that always use .RenderPartial() rather than .Partial() for best performance in .cshtml view. I used Model.Categories instead of mymodel.Categories because of the strongly typed Index.cshtml already have #model mymodel in the begining of the file.
In my practise I have the models like:
Model.Departments - IList<DepartmentModel>
Model.SelectedDepartment - DepartmentModel
Model.Employees - IList<EmployeeModel>
which is used in:
#{Html.RenderPartial("DepartmentMenu", Model.Departments);}
<div id="employeeViewContainner">
#foreach (var emp in Model.Employees)
{
Html.RenderPartial("CardView" + Model.SelectedDepartments.Name, emp);
}
</div>
This will render employee list with different skin for different department.

Resources