Create controller for partial view in ASP.NET MVC - asp.net

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

Related

MVC 4 - pass data via ViewBag to _Layout partial view

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.

Using ViewBag in Layout page

I have my _Layout.cshtml file that uses the ViewBag model to render some dynamic content.
I understand ViewBag can be populated in the controller and accessed in the view and/or layout page.
My question is, if my Layout page is using #ViewBag.SiteName, I want to avoid having to set this variable in each controller before I return the view. Is there a way to set this variable on a global level? Or how else should I pass this data to the layout page?
If you set anything in ViewBag - this happens after the Layout has been rendered -
You've missed the boat.
As others have mentioned, you can create a "helper" controller:
public class LayoutController : BaseController
{
[ChildActionOnly]
public ActionResult SiteName()
{
return new ContentResult {Content = "Site name goes here"};
}
}
Then, in your layout:
#{Html.Action("SiteName", "Layout")}

Having two master pages in mvc 3 application?

I have a MVC application that exist at the moment using a _MainLayoutPage for its Master Page.
I want to create another Master Page for a different purpose. I will be creating a new controller as well.
How can I do this?
The simplest way is in your Action Method, set a Viewbag property for your Layout
public ActionResult Index()
{
ViewBag.Layout= "~/Views/Shared/layout2.cshtml";
In your View, set the layout property
#{
Layout = #ViewBag.Layout;
}
In _ViewStart.cshtml, put this:
#{
try {
Layout = "~/Views/" + ViewContext.RouteData.Values["controller"] + "/_Layout.cshtml";
}
catch {
Layout = "~/Views/Shared/_Layout.cshtml";
}
}
And then you can put a controller specific _Layout.cshtml in your controller folders, like
~/Views/User/_Layout.cshtml for a controller named UserController
~/Views/Account/_Layout.cshtml for a controller named AccountController
And because of the try/catch, it will fall back to the '~/Views/Shared/_Layout.cshtml' layout if one is not defined for a specific controller.

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.

Html.ActionLink in Partial View

I am using the following code in my master page:
<% Html.RenderAction("RecentArticles","Article"); %>
where the RecentArticles Action (in ArticleController) is :
[ChildActionOnly]
public ActionResult RecentArticles()
{
var viewData = articleRepository.GetRecentArticles(3);
return PartialView(viewData);
}
and the code in my RecentArticles.ascx partial view :
<li class="title"><span><%= Html.ActionLink(article.Title, "ViewArticle", new { controller = "Article", id = article.ArticleID, path = article.Path })%></span></li>
The problem is that all the links of the articles (which is built in the partial view) lead to the same url- "~/Article/ViewArticle" .
I want each title link to lead to the specific article with the parameters like I'm setting in the partial view.
Thanks.
I think your not using the ActionLink correctly. Change the ActionLink code to:
Html.ActionLink(
article.Title,
"ViewArticle",
"Article", // put the controller here
new
{
id = article.ArticleID,
path = article.Path
},
null)
Notice the null at then end.
EDIT: Why are you using [ChildActionOnly] in your controller? Since it is an MVC 2 feature I am assuming that you are using MVC2? Try removing it and check out the following article:
http://www.davidhayden.me/2009/11/htmlaction-and-htmlrenderaction-in-aspnet-mvc-2.html
I think the issue has to do with your partial not rendering. I would start by just trying to verify that your partial is rendering properly. Once you confirm that start to debug why the partial is not outputing.
I was able to solve the problem by using the following call in my RecentArticles action:
return PartialView("~/Views/Shared/Article/RecentArticles.ascx", viewData);
It seems like the partial view was not being rendered at all,
Thanks !

Resources