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.
Related
I want to insert a partial view from NewsController into HomeController.
In my NewsController
public ActionResult LastNewsPatial()
{
var lstLastNews = db.Articles.Take(5).OrderByDescending(m => m.CreatedDate).ToList();
return PartialView(lstLastNews);
}
In Views/News folder i create LastNewsPatial.cshtml
#model IEnumerable<mytest.com.Models.Article>
#foreach (var item in Model) {
<div class="glidecontent">
<img src="#item.ImageURL" style="float: left; margin-right:21px;" />
<strong>#item.Title</strong><br /><br />
#item.Content
</div>
}
In Views/Home/Index.cshtml i insert a LastNewsPatial view
#Html.Partial("~/Views/News/LastNewsPatial.cshtml")
When i run my project then I received a error
Object reference not set to an instance of an object.
at row
#foreach (var item in Model)
in LastNewsPatial.cshtml
How can I fix it?
The reason for your error is that the Model isn't passed to the view... infact the action isn't even called at all. Set a breakpoint and you'll confirm this.
I think you should be calling #Html.RenderAction inside the index instead of #Html.Partial.
Instead of #Html.Partial("~/Views/News/LastNewsPatial.cshtml")
use
#Html.RenderAction("LastNewsPatial","News")
or #Html.Action("LastNewsPatial","News")
You don't need to give .cshtml in name of view when rendering a view. Only give name of PartialView without .cshtml like this.
#Html.Partial("~/Views/Shared/LastNewsPatial")
You can also use #Html.Action() to render your view like this
#Html.Action("LastNewsPatial","News")
This worked for me: #Html.Partial("../Views/Shared/LastNewsPatial")
The .. instead of the ~
Some of the answers here are missing the question
You use this
#Html.Partial("~/Views/News/LastNewsPatial.cshtml")
Which creates a partial view without a model. So in order for this to work you need to pass model.
#Html.Partial("~/Views/News/LastNewsPatial.cshtml", your_model)
Your problem is not view related and you simply pass no object to the partial. How to do it is up to you. Do you want a Html.Partial or Html.Action? Depending on your needs.
P.S.
Use RenderPartial and RederAction they are better practices since Partial and Action return an HTML string where instead using render allows ASP.NET to write directly to the response stream.
I have a _Layout.cshtml files as a partial view as header on each main view.
I would like to define a Select element on the _Layout and pass some data to the partial view using ViewBag so that the data is populated on the view and can later be submitted.
My questions are:
Where is the ActionResult function defined that contains and defines the data in ViewBag?
What do I do if I want to submit a form on the partial view? Where and which action should be defined/used to accept the HttpPost command?
Thanks!
What I suggest is to make a base controller class.
Inherit all your controllers from it.
The code to render the data for the layout, can lie in it's constructor, or some other common function that all your controllers can use as children of this base class.
public class BaseController : System.Web.Mvc.Controller
{
public BaseController()
{
// This code will run for all your controllers
ViewBag.MyData = "SomeData";
}
}
About your question:
What do I do if I want to submit a form on the partial view? Where and which action should be defined/used to accept the HttpPost command?
You can just put the controller name on your form:
#using (Html.BeginForm("ActionName", "Controller"))
There are possibly a few misunderstandings about how _layout.cshmtl and partial views work:
_layout.cshtml is not a partial view. It is the layout template used by all your pages. It is kind of the "outer" of the page. It is automatically applied (except if you set Layout = null). A partial view in turn is the "inner" of the page. You call it explicitly from your page using #Html.Partial.
Even though your page is rendered by multiple views - the actual view, the layout, maybe some partial views - it is still the result of a single controller action. (Except if you use #Html.Action for rendering partial "actions"). Also, the page rendered is a single HTML page, that is, any forms on the page are simply HTML forms.
Therefore, the answer to "where is the ActionResult function defined" is: In the action that you want your page to be rendered for.*
The answer to "Which action should be used to accept the HttpPost command" is the same as if the form was on your page: You can define an arbitrary action on an arbitrary controller for receiving the form. You just need to refer to that action when you render the form:
#using (Html.BeginForm("action", "controller")) { ... }
*) If you want to prevent having to build the select list in each and every controller action that relies on _layout, you could conceivably use #Html.Action. That is, you define a "partial action" which is nothing else than a controller action that returns a PartialView() and a partial view to render the model from that action. Then you can use that partial action to build the select list.
However having read some news about ASP.NET vNext, partial actions seem not to be liked to much by the community and in vNext there will be another way to achieve the same.
Still if you want to go this way this enables you to separate the logic for your dropdown (language? user menu?) from your other actions and views:
class UserController
{
PartialViewResult UserMenuDropdown()
{
return PartialView(BuildUserMenuFrom(.....));
}
[Post]
ActionResult PostUserMenu()
{
// do whatever you want once the form is posted
}
}
In your _layout you call the partial action:
#Html.Action("UserMenuDropdown", "User")
And in the view for UserMenuDropdown you render the form:
#using (Html.BeginForm("PostUserMenu"))
{
#Html.DropDownListFor(m => m.UserMenuSelectList)
}
This way your dropdown list becomes a "first class member", with its own controller action, main view, and model. You don't need a ViewBag for this, and you don't have to build the select list in each and every controller action.
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
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
How can I create an individual controller and model for a partial view? I want to be able to place this partial view any where on the site so it needs it's own controller. I am current rendering the partial as so
#Html.Partial("_Testimonials")
Why not use Html.RenderAction()?
Then you could put the following into any controller (even creating a new controller for it):
[ChildActionOnly]
public ActionResult MyActionThatGeneratesAPartial(string parameter1)
{
var model = repository.GetThingByParameter(parameter1);
var partialViewModel = new PartialViewModel(model);
return PartialView(partialViewModel);
}
Then you could create a new partial view and have your PartialViewModel be what it inherits from.
For Razor, the code block in the view would look like this:
#{ Html.RenderAction("Index", "Home"); }
For the WebFormsViewEngine, it would look like this:
<% Html.RenderAction("Index", "Home"); %>
It does not need its own controller. You can use
#Html.Partial("../ControllerName/_Testimonials.cshtml")
This allows you to render the partial from any page. Just make sure the relative path is correct.
If it were me, I would simply create a new Controller with a Single Action and then use RenderAction in place of Partial:
// Assuming the controller is named NewController
#{Html.RenderAction("ActionName",
"New",
new { routeValueOne = "SomeValue" });
}
The most important thing is, the action created must return partial view, see below.
public ActionResult _YourPartialViewSection()
{
return PartialView();
}
You don't need a controller and when using .Net 5 (MVC 6) you can render the partial view async
#await Html.PartialAsync("_LoginPartial")
or
#{await Html.RenderPartialAsync("PartialName");}
or if you are using .net core 2.1 > you can just use:
<partial name="Shared/_ProductPartial.cshtml"
for="Product" />
Html.Action is a poorly designed technology.
Because in your page Controller you can't receive the results of computation in your Partial Controller. Data flow is only Page Controller => Partial Controller.
To be closer to WebForm UserControl (*.ascx) you need to:
Create a page Model and a Partial Model
Place your Partial Model as a property in your page Model
In page's View use Html.EditorFor(m => m.MyPartialModel)
Create an appropriate Partial View
Create a class very similar to that Child Action Controller described here in answers many times. But it will be just a class (inherited from Object rather than from Controller). Let's name it as MyControllerPartial. MyControllerPartial will know only about Partial Model.
Use your MyControllerPartial in your page controller. Pass model.MyPartialModel to MyControllerPartial
Take care about proper prefix in your MyControllerPartial. Fox example: ModelState.AddError("MyPartialModel." + "SomeFieldName", "Error")
In MyControllerPartial you can make validation and implement other logics related to this Partial Model
In this situation you can use it like:
public class MyController : Controller
{
....
public MyController()
{
MyChildController = new MyControllerPartial(this.ViewData);
}
[HttpPost]
public ActionResult Index(MyPageViewModel model)
{
...
int childResult = MyChildController.ProcessSomething(model.MyPartialModel);
...
}
}
P.S.
In step 3 you can use Html.Partial("PartialViewName", Model.MyPartialModel, <clone_ViewData_with_prefix_MyPartialModel>). For more details see ASP.NET MVC partial views: input name prefixes