Html.RenderAction and Html.Action

One of the upcoming new features being added to ASP.NET MVC 2 Beta is a little helper method called Html.RenderAction and its counterpart, Html.Action. This has been a part of our ASP.NET MVC Futures library for a while, but is now being added to the core product.

Both of these methods allow you to call into an action method from a view and output the results of the action in place within the view. The difference between the two is that Html.RenderAction will render the result directly to the Response (which is more efficient if the action returns a large amount of HTML) whereas Html.Action returns a string with the result.

For the sake of brevity, I’ll use the term RenderAction to refer to both of these methods. Here’s a quick look at how you might use this method. Suppose you have the following controller.

public class MyController {
  public ActionResult Index() {
    return View();
  }
  
  [ChildActionOnly]
  public ActionResult Menu() {
    var menu = GetMenuFromSomewhere();
      return PartialView(menu);
  }
}

The Menu action grabs the Menu model and returns a partial view with just the menu.

<%@ Control Inherits="System.Web.Mvc.ViewUserControl<Menu>" %>
<ul>
<% foreach(var item in Model.MenuItem) { %>
  <li><%= item %></li>
<% } %>
</ul>

In your Index.aspx view, you can now call into the Menu action to display the menu:

<%@ Page %>
<html>
<head><title></title></head>
<body>
  <%= Html.Action("Menu") %>
  <h1>Welcome to the Index View</h1>
</body>
</html>

Notice that the Menu action is marked with a ChildActionOnlyAttribute. This attribute indicates that this action should not be callable directly via the URL. It’s not required for an action to be callable via RenderAction.

We also added a new property to ControllerContext named IsChildAction. This lets you know whether the action method is being called via a RenderAction call or via the URL.

This is used by some of our action filters which should do not get called when applied to an action being called via RenderAction such as AuthorizeAttribute and OutputCacheAttribute.

Passing Values With RenderAction

Because these methods are being used to call action methods much like an ASP.NET Request does, it’s possible to specify route values when calling RenderAction. What’s really cool about this is you can pass in complex objects.

For example, suppose we want to supply the menu with some options. We can define a new class, MenuOptions like so.

public class MenuOptions {
    public int Width { get; set; }
    public int Height { get; set; }
}

Next, we’ll change the Menu action method to accept this as a parameter.

[ChildActionOnly]
public ActionResult Menu(MenuOptions options) {
    return PartialView(options);
}

And now we can pass in menu options from our action call in the view

<%= Html.Action("Menu", 
  new { options = new MenuOptions { Width=400, Height=500} })%>

Cooperating with the ActionName attribute

Another thing to note is that RenderAction honors the ActionName attribute when calling an action name. Thus if you annotate the action like so.

[ChildActionOnly]
[ActionName("CoolMenu")]
public ActionResult Menu(MenuOptions options) {
    return PartialView(options);
}

You’ll need to make sure to use “CoolMenu” as the action name and not “Menu” when calling RenderAction.

Cooperating With Output Caching

Note that in previous previews of the RenderAction method, there was an issue where calling RenderAction to render an action method that had the OutputCache attribute would cause the whole view to be cached. We fixed that issue by by changing the OutputCache attribute to not cache if it’s part of a child request.

If you want to output cache the portion of the page rendered by the call to RenderAction, you can use a technique I mentioned here where you place the call to RenderAction in a ViewUserControl which has its OutputCache directive set.

Summary

Let us know how this feature works for you. I think it could really help simplify some scenarios when composing a user interface from small parts.

What others have said

Requesting Gravatar... Kazi Manzur Rashid Nov 18, 2009 2:35 AM
# re: Html.RenderAction and Html.Action
Excellent, a long awaited features that gets included in the core. So the parent action filters does not get executed in this version?
Requesting Gravatar... Steve Nov 18, 2009 3:14 AM
# re: Html.RenderAction and Html.Action
One more suggestion, and admittedly this doesn't come from someone who's using ASP.NET MVC right now, so there might already be a way to do this. But it overlaps with some work I've been doing on a different framework so I thought I'd mention it...

Since you already have a good typeless URL -> typed parameter parser, why not allow the passing of options as an URL string? In other words,
<%= Html.Action("Menu", "width=400&height=500") %>

This allows you to create composable subparts without the burden of strict typing, for instance:
<%= Html.Action("Menu", "width=" + Request.Params["menu.width"] + "&height=" + Request.Params["menu.height"]) %>

That in turn could have a params-based wrapper so that it could be written as <%= Html.Action("Menu", Passthru("width","height")) %>

The end result is that you don't need to use endless generic wrappers to do simple operations - on data that began its life as a generic string anyway.

In general I'd love to see more of this, where mvc takes on the strengths of dynamic templating languages like PHP, with the power of typed actions when you need them.
Requesting Gravatar... Jeffrey Palermo Nov 18, 2009 4:19 AM
# re: Html.RenderAction and Html.Action
Very cool to include it in the core product. I really like Html.Action() as well. Good job, Phil. Keep up the great work. You are changing the world of ASP.NET development!
Requesting Gravatar... Will Green Nov 18, 2009 4:36 AM
# re: Html.RenderAction and Html.Action
Calling back into the controller from the view just doesn't feel quite right to me. Wouldn't it be better to instead create a View Model that encapsulates all the specific, for lack of a better term, "sections" of the view to be the rendered? The main view would then call the partials, passing in specific parts of the View Model that each partial needs.

How you assemble that view model is of course up to you (non-action methods in the controller, perhaps?), so you'd still get the re-use. You should still be able to cache the output of the partials, too.

Or am I totally missing the point?
Requesting Gravatar... ParseInt Nov 18, 2009 4:57 AM
# re: Html.RenderAction and Html.Action
@Will Green, I totally agree with you, in all our projects we're using a base ViewModel that all ViewModels inherits from, and if there's something we need (eg a menu) to be displayed on all pages we just create an actionfilter that fills that menu in the baseview. You get the same power as the Html.Action and you get the separation you're used too. And you dont get the performance hit you get with Html.RenderAction (which was pretty significant the last time i checked in MVC Futures using JetBrains DotTrace).

@Phil: Is the performance problem fixed in this version?

My two cents..
Requesting Gravatar... Scriov Nov 18, 2009 5:20 AM
# re: Html.RenderAction and Html.Action
Phil,

Something like this is just what we've been looking for, but having seen the implementation I'm not sure...

We've been looking for a way to display multiple sets of data on one page. Assume the page is a Products list, but in the UI somewhere there is also a Private Messages toolbar.

Currently, the only way to do this (to my knowledge) is for the single action to populate the ViewData for Products & Private Messages which is no good.

We were hoping RenderAction would solve the issue:

ProductsController
Index();

MessagesController
Index();
Summary(); // Lets say this is the one that we'd want to use,
// it sets it's own View Data so that
// Products/Index doesn't have to

Then, within a view somewhere one could have:

Html.RenderAction("Messages", "Summary");

But it doesn't sound like we'd be able to set a controller name?

Others above have suggested using a ViewModel and Action Filter to set data in said model. This (to me) doesn't sound right either. It should be actions that set data, not passive, out-of-the-way
places like Action Filters (not to mention in a modular application one couldn't easier have a base ViewModel anyway; each component could, in theory, plug into a different MVC Shell app and this shell have may not have Private Messages so it wouldn't make sense to have a view model that represents that. Same goes for inherited controllers that have the MessageSummary action built in; it's not valid in every use of the module.)

Again, I'm looking to focus on Actions having a single responsibility which is to populate their data, not data for all of the other components on the screen (it is the Products action afterall, not ProductsAndMessages).

Does this make sense? Does it sound too crazy? Is there already an easy solution to this in MVC?

Cheers,
Scriv
Requesting Gravatar... Lee Smith Nov 18, 2009 5:37 AM
# re: Html.RenderAction and Html.Action
There's something not right about views calling controllers.

Use Partial Requests!

blog.codeville.net/.../partial-requests-in-aspn...
Requesting Gravatar... Mohamed Meligy Nov 18, 2009 6:38 AM
# re: Html.RenderAction and Html.Action
This is great, Haack :)

How about expression-based overloads of this?

Would love to be able to write something like:
<%= Html.RenderAction(
(MyController c) =>
c.Menu( new MenuOptions(){Width=400, Height=500} )
) %>

Is that expected to see sometime soon?
Requesting Gravatar... Dmitriy Nagirnyak Nov 18, 2009 8:03 AM
# re: Html.RenderAction and Html.Action
I think IsChildAction is not such a good property.

It does not represent nested child-actions.

Better one would probably be "ParentContext" or similar, so we can track the nested calls easily this way.
Requesting Gravatar... asper Nov 18, 2009 9:02 AM
# re: Html.RenderAction and Html.Action
why do we have 2 ways on doing same thing? we don't have Html.Partial('control') where it returns a string of the control.
Requesting Gravatar... Qun Nov 18, 2009 11:46 AM
# re: Html.RenderAction and Html.Action
Hi,
Ive two controllers from different area.
I cannot use renderaction method.

did I missed something, or this is a bug?

thanks
Requesting Gravatar... Haacked Nov 18, 2009 12:00 PM
# re: Html.RenderAction and Html.Action
@Steve, you can do this already.

<%= Html.Action("Menu", new {width=400, height=500"}) %>
Requesting Gravatar... Haacked Nov 18, 2009 12:01 PM
# re: Html.RenderAction and Html.Action
@Scriov I'm not sure I understand. You can set the controller name. I didn't show every overload in my blog post.
Requesting Gravatar... Haacked Nov 18, 2009 12:02 PM
# re: Html.RenderAction and Html.Action
@asper, we do have Html.Partial now. Use Render***(...) if you're rendering a large subview. It's more efficient.

Use Html.Action or Html.Partial if it's important to you to have a string returned (for unit test purpose or if your view engine doesn't support the Render*** methods.
Requesting Gravatar... Haacked Nov 18, 2009 12:07 PM
# re: Html.RenderAction and Html.Action
@Qun you need to specify the area parameter just like you would if you were using Html.ActionLink.

<%= Html.Action("Menu", new {area="MyArea"}) %>
Requesting Gravatar... asper Nov 18, 2009 12:10 PM
# re: Html.RenderAction and Html.Action
html.Partial now? cool! 2.0 is gonna be great
Requesting Gravatar... Shiju Varghese Nov 18, 2009 12:34 PM
# re: Html.RenderAction and Html.Action
Phil, This is really COOL.
Requesting Gravatar... Steve Nov 18, 2009 12:37 PM
# re: Html.RenderAction and Html.Action
Not sure you understood my point; I realize you can wrap it in an anonymous class rather than putting it in an explicitly declared one, which is a good start. But is there a way to hook in to the request parsing you've built for actions, when delegating to another action? There are all sorts of reasons that's useful - mainly, being able to pass through URL values without having to understand them. Otherwise you're stuck with:

<%= Html.Action("Menu", new { width = int32.Parse(Request.Param["width"]), etc...
Requesting Gravatar... Ali Nov 18, 2009 2:21 PM
# re: Html.RenderAction and Html.Action
glad to hear added to the core product.
happy MVCing !
Requesting Gravatar... Gina Nov 18, 2009 2:31 PM
# re: Html.RenderAction and Html.Action
Hi,

I really enjoy ASP.NET MVC, and I've been using Html.RenderAction since it's first arrival in the futures assembly, but after testing it's still a big performance hit, is this something that will be fixed in the final version?

Thanks
Requesting Gravatar... Haacked Nov 18, 2009 4:13 PM
# re: Html.RenderAction and Html.Action
@Gina we will investigate the performance hit and try to mitigate it as much as possible. Please consider what you're asking RenderAction to do.

Basically it's very similar to making another request since we need to run through routing to make sure we have the appropriate route data and context to call the action method. So each call to RenderAction is going to add up.
Requesting Gravatar... born2code Nov 19, 2009 3:15 AM
# re: Html.RenderAction and Html.Action
so i have been doing this for a while now by simply using jquery to call my PartialResult controller method and setting the html with the PartialView that comes back.
does the exact same thing with the added benefit of async loading the partial view, without having to rely on a separate RenderAction method.
Requesting Gravatar... Jørn Schou-Rode Dec 13, 2009 5:35 PM
# re: Html.RenderAction and Html.Action
I am currently using the RenderAction helper method from the futures assembly, and one problem I have with it, is that it always creates a new instance of the controller class, even when rendering child actions from the controller already in action. If the controller has an expensive initialization (constructor or Initialize method), using RenderAction causes an unnecessary overhead.

Is the behavior the same in ASP.NET MVC 2? Couldn't RenderAction just invoke the child action on the already instantiated controller, in the case where the type of the parent and child controller is the same?
Requesting Gravatar... Robert Koritnik Dec 28, 2009 4:12 PM
# re: Html.RenderAction and Html.Action
ChildActionOnlyAttribute to let through Ajax requests.

As I've skimmed this attribute's code it seems that it only passes through requests when a certain action is called using Html.Action/RenderAction. This is probably not exactly how it should work. It should also pass through all Ajax requests because we could make an Ajax request to return a partial view.

Forms in partial views
Let's say I have a controller that returns a partial view with a comments form. I call this controller action within NewsPost.aspx view which also has a general partial view "Quick Search" displayed on the same page with a separate form element. If I write


using(Html.BeginForm())
{
...
}


in both partial views (rendered with RenderAction()), where will those two views post back? To the URL of the main view maybe?

So if I set a certain controller and action in my BeginForm() will still present me with other problems, since I won't know what to return from my controller action... I don't know which view vas displayed on the client to return the same one, and returning a partial view won't work either, since it was a full page post...

How do I do this kind of scenarios?
Requesting Gravatar... haacked Dec 30, 2009 1:55 AM
# re: Html.RenderAction and Html.Action
Don't use that attribute. It's not required. It is meant to block direct web requests. Ajax is a direct web request.
Requesting Gravatar... haacked Dec 30, 2009 5:00 AM
# re: Html.RenderAction and Html.Action
Regarding forms in partial views, I generally don't put the form in the partial view. I'll do something like this:

<% using(Html.BeginForm()) { %>
<% Html.RenderPartial("Comments"); %>
<% } %>

Otherwise, you'll have to add something extra to the form to tell the controller what it should do.
Requesting Gravatar... Robert Koritnik Dec 30, 2009 6:04 AM
# re: Html.RenderAction and Html.Action
Well, using forms in partial views and displaying them using RenderPartial() is a minor issue, because there's still only one controller action being executed.

Things tend to get more messy when you display partial views using RenderAction(). When you'd use Html.BeginForm() it wouldn't add action attribute to it, so all would post back to your main view. But even if you had provided some other controller and action, it wouldn't work, because your action can (and in some cases it has to) return a PartialView. This action would be executed as a result of an HTTP request and not as a result to RenderPartial() call.

Now think of returning something back? What? A view! But how would you know which one if your action is being called within various different pages?

I think that avoiding FORM element usage in partial views isn't a solution here. Let me explain why by a practical example:

Your site has two major "controls" (actions that are called by RenderAction() and return PartialView), that are part of every view: Logon with two inputs and a button and a Quick search with a text box and a button.

1. you can't have both controls in the same form, because there might be another form as the main page content (let's say someone is adding a comment to a post that's being displayed in the view)

2. Having them all contained within the same form means submitting a comment would result in sending comment, login and search values as well. What if I had entered something in the search box and then entered comment fields and submitted them? What should I do then? Display search results or add a comment?

3. Having them in separate form elements solves the problem. Clicking on Search button takes me to search results. This one is easy, because search results probably are going to be displayed in a completely separate view.

4. But what happens when a user logs in? You have your blog post page with comment form. It displays a particular post related by route values. Then your user decides to log in. They enter their credentials and click "Login" submit button. Form action would be set to go to ../Account/Login. Ok. So the call processes form values and authenticates user. But what's next? This action should return a partial view with user information that would be displayed in place of a login form. But it can't because there was a full page postback. It has to display the same page that was displayed previously.

I hope that by this very common everyday example I've explained the problem of RenderAction() helper. It's the right tool for the job, but it doesn't quite provide the necessary functionality.

I have an idea how this should work, but I haven't tested it thoroughly yet...

The thing is that all form should post back to the main view's URL. Every form element should also have a hidden input with data about the actual controller+action[+additional route values] of the form. When the page is being processed, only the control (rendered by RenderAction()) that's related to this controller+action provided in the hidden field should process as a POST request. All others should only be renedered as GET.

1. Main view will get displayed as a GET call to some action.
2. Some form that is posting data will be processed with POST.
3. Other forms will be processes as GET.

This would be possible, because RenderAction() does initiate a new MvcHandler with generated http context (including request).

Drawbacks:

1. Messing around with hidden fields reminds me a bit of web forms...
2. All forms (as FORM elements) should be part of partial views displayed in the main view by RenderAction().

I guess everything else should work as expected.
Requesting Gravatar... haacked Dec 30, 2009 7:25 AM
# re: Html.RenderAction and Html.Action
I'd argue your login form should post to a login action which redirects back to the original form, if your login is a normal form post. If it's an Ajax login, just return a login status view. In that case it doesn't matter where it's called from.

I think you may be over thinking this.
Requesting Gravatar... Robert Koritnik Dec 30, 2009 7:56 PM
# re: Html.RenderAction and Html.Action
Maybe so... But the thing is that this application should also work script free. So Ajax calls may not be allowed.

My example was taken from the real world, but may not have been the perfect one. The case of a login control at the top of the page would usually work Ajax style. You're right.

Anyway. While I was thinking about this hidden input and relating to web forms it made me think a bit. MVC 1 is already using hidden fields when you display checkboxes, so hidden fields may not be the wrong way of doing this.

I still think that MvcHandler + Html.BeginForm would change things significantly if they worked as I suggested. Something I should play with I guess. And I don't think this would overcomplicate the framework too much.

BTW: How did other MVC frameworks solve this?
Requesting Gravatar... haacked Dec 31, 2009 2:07 AM
# re: Html.RenderAction and Html.Action
If JavaScript is turned off, then the form is fully posted to the login action and redirected back to the URL you were on. I believe this is how other MVC frameworks do it because they don't have the equivalent of RenderAction AFAIK.
Requesting Gravatar... Roger Rogers Jan 07, 2010 4:06 AM
# re: Html.RenderAction and Html.Action
> I'd argue your login form should post to a login action which redirects back to the original form

I try to use return Redirect("..."); in the RenderAction, but I get the error "Child actions are not allowed to perform redirect actions."

How can I make my RenderAction action return a redirect so that I can use the PRG pattern?

(MVC newbie, so forgive me if the answer is obvious)
Requesting Gravatar... Roger Rogers Jan 08, 2010 2:11 AM
# re: Html.RenderAction and Html.Action
Nevermind, this works as expected, e.g.:

return RedirectToAction("Details", "Steps");

Not sure what I was doing wrong, but the error is gone.
Requesting Gravatar... bill xie Jan 22, 2010 3:02 AM
# re: Html.RenderAction and Html.Action
Sometimes this would be useful like Server.Execute in WebForms. But from the standpoint of architecture this sounds not a good approach since action like Menu is only a portion of View and should stay with View only. Unfortunately it produces an opportunity to allow developers to put View into Controller and thus may be a nightmare for change and maintenence.

On the other hand, a Web Form can encompass lots of postback triggers which corresponds to action in the MVC framework. Looking at View, so many actions in a Web Form share the same view. I am afraid this is difficult in the MVC framework. User control can mitigate this job. However, it is still a question how to write less Views or have actions share the same view.
Requesting Gravatar... Joe Future Jan 28, 2010 12:31 PM
# re: Html.RenderAction and Html.Action
In my app, I have extensions that know how to render their own configuration forms. I've been using RenderAction to call the "configure" action on each extension's controller, which renders the configuration form view. I'd like to have the extensions' controllers handle the form submit as well and then redirect back to the "manage all extensions" admin page. I was able to do this in MVC v1, but because of the same issue Roger Rogers mentioned above, the child controllers can't redirect now. What should I return from the extension controller's action that handles the form submit?
Requesting Gravatar... Ta Duy Duc Feb 03, 2010 10:24 AM
# The ultimate performance issue
Hi haacked. today i have a chance to test Html.Action on my controller, the concept is great for rendering static content, like menu, or cached content that you can cached, but like the before html.RenderAction, it's having the same problem issue on running too sync.

I tried to run a Html.action on my page with a query in the action that im trying to call. The page have setup 5 Html.action call. My result in my profiler is:

Async 1 - From main Controller - 3.1s
Async 2 - From main Controller - 2.2s
Async 3 - From main Controller - 2.4s
-------------------------------------
The total operate cost here is only ~3s since it's async
Sync 1 - Html.Action - 2s
Sync 2 - Html.Action - 2s
Sync 3 - Html.Action - 2s
Sync 4 - Html.Action - 2s
Sync 5 - Html.Action - 2s
-> in here is another 10s to run

This issue is always the issue of calling Action in Controller. I guess unless you can render the whole asp.net view and then detect Html.action, run them async, then render the final output. I believe there is almost no way to make this sufficient.
Requesting Gravatar... Tim Apr 21, 2010 12:18 PM
# re: Html.RenderAction and Html.Action
Hi All,

I was just wondering if I can wrap the RenderAction method within an extension method, so I can do a bit more processing in the htmlhelper before calling the RenderAction.

System.Web.Mvc.Html.ChildActionExtensions.RenderAction(helper, "account", "login");


called in my own htmlhelper still calls the method using the control and action of the current request...

Any ideas why?
Requesting Gravatar... Igor Dec 16, 2010 2:59 PM
# re: Html.RenderAction and Html.Action
what if i want to display a sub menu? the scenario is like this: in an site master, i have a div where i render all the menu items (on top of the site) and a submenu, (somewhere on the left). i guess they have to be in a site master, as this is the only way to make the menus visible throughout the whole page.

when i call render action method, i have a problem of filtering the submenu items depending on the menu item selected currently, as every source of routevalue collection returns me values of the action executed currently, not the one that initiates that action.
Requesting Gravatar... hhgregg Feb 03, 2011 9:56 AM
# re: Html.RenderAction and Html.Action
So...help me understand the differences between Html.RenderAction and Html.Action. Is it that Html.RenderAction will render the result directly to the Response (which is more efficient if returning a large amount of HTML). Whereas Html.Action returns a string with the result. Is this correct?
Requesting Gravatar... haacked Feb 04, 2011 6:40 AM
# re: Html.RenderAction and Html.Action
@hhgregg That's correct.
Requesting Gravatar... Mike Vanderkley Aug 09, 2011 7:53 AM
# re: Html.RenderAction and Html.Action
Hi Phil, I have a route class which returns a RouteValueDictionary. This var includes area/controller/action. So for methods like Html.ActionLink and Html.BeginForm; I have made extension so action defaults to null. So I endup with en extention where I can do @Html.ActionLink("Edit", NewsRoutes.Edit(news)).

However, I cannot make a similar extention for RenderAction as the action param is required. This example does not work.
public static void RenderAction(this HtmlHelper helper, RouteValueDictionary routeValues) { helper.RenderAction(null, routeValues); }

This appears inconsitent with ActionLink and BeginForm. (Mvc3 btw).
Requesting Gravatar... Ben Mar 19, 2012 12:44 PM
# re: Html.RenderAction and Html.Action
Hi Phil

why there is no Html.Route(routeName: "MyRouteName", new { /*data*/ }

?
Requesting Gravatar... Mark May 01, 2012 10:33 PM
# re: Html.RenderAction and Html.Action
Hi Phil,
Can you share the overload to use a different controller please?

I have been looking into this for hours. Looks like you have to pass in the values from the parent view at load and use the viewbag, but that doesnt seam very dynamic. I would be happy to just fire the partial view and pass a parameter to load the child records. Seams to me that partial views would almost always have a different controller and would often have related child records.

Thanks, have been reading lots of your posts. Very helpful.
Requesting Gravatar... haacked May 07, 2012 3:44 PM
# re: Html.RenderAction and Html.Action
@Mark not sure I understand your question.

What do you have to say?

(will show your gravatar)
Please add 7 and 2 and type the answer here: