Html.RenderAction for ASP.NET MVC 1.0

24 September 2009

The ASP.NET MVC 1.0 Futures assembly (that is not included with ASP.NET MVC 1.0) has a powerful HtmlHelper extension method called RenderAction().   It sounds similar to the Html extension method called RenderPartial() for a good reason.  RenderAction() executes an action on a controller, allowing you to move your logic out of the views.

The problem with Html.RenderPartial()

There is no direct problem with this.  It is just that developers are left with only the ability to render a Partial View.  What if you want that partial view to act on model properties you pass into that partial view?  What if you need to access the ambient values in the Routes collection to render some specifics?  Unfortunately, developers are only left with RenderPartial() which only gives you access to a partial view.  If you really need this logic, you have no choice but to put it into the partial view.  Or, to put it into your controller’s action method that calls the view that calls the partialviews.  Needless to say, that’s a bit hokey.

What Html.RenderAction<TController>() Resolves

It gives you the power of moving that complex View and Partial View logic to a Controller’s Action, where it belongs!  Think of it as a “Render Partial Action” method of where you can call back into a controller to render some logic. This gives you the ability to clean up your partial views now by removing that logic and placing it on a controller’s action, that renders the partial view when done. The syntax looks like:

<% Html.RenderAction<ProductController>(c => c.RenderProductResults()); %>
<!-- Or... //-->
<%= Html.RenderAction("RenderPartialResults", "Product"); %>

Put simply, this renders an action on a controller directly in your view.  You may be thinking “big deal”, but I assure you this is a big deal in large complex MVC sites.  Having the power to abstract or breakup your controller’s actions into multiple partial-actions and multiple views is very powerful.  Instead of relying on the 1 controller' action to wire up all of the data for all of the views, partial views, and logic for the views.  Now, you can just focus on that one section - and abstract the rest into reusable parts.

You may be wondering where this extension method is with your ASP.NET MVC project.  As mentioned above, it is part of the Futures extension of the ASP.NET MVC project at codeplex.

ASP.NET MVC Sidebar Widget Example

The current pattern suggests you use Html.RenderPartial to render those partial views as your sidebar widgets.  What if you want those widgets to be more complex?  What if you want those widgets to act on the the current Route?  Well, you are left with little options with RenderPartial.  Instead, you want to use RenderAction to call an action on a controller to handle that logic. 

Assume we are viewing a blog post entry and on the right, we want a sidebar widget for related posts. To do this, first create an action on a controller called RelatedPosts().

public ActionResult RelatedPosts(Int32 postID)
{       
// some complex logic, or simple logic, can go here now…
//

if (postID < 1)
return PartialView(“NoRelatedPosts”);
  var relatedPosts =
    _postService.FetchRelatedPosts(postID);

  if (relatedPosts.Count > 0)
   return PartialView("RelatedPosts", relatedPosts);
  else
    return PartialView(“NoRelatedPosts”); }

Notice how the logic here accounts for empty or no results, and returns an a different partial view?  Using RenderPartial(), this is logic could only be reflected with inline IF ELSE brackets within your PartialView - and ugly spaghetti mess.  Also, how would you even retrieve the related posts collection?  You have no choice but to obtain that collection back on the post entry view action - which should not be concerned about our little sidebar widget.  All it should do is wire up the post.  But, that is not the case with RenderPartial() - you have to wire up all of your data ahead of time in one large ViewModel with multiple entities dangling off of it.

No no.  Let’s do it with RenderAction() and instead put this logic into the Controller (where it belongs, so we can test for it); and then, we can call it with the RenderAction() extension.  We can do this now with a simple call within our larger post view like so:

<% Html.RenderAction<PostController>(c => c.RelatedPosts(Model.PostID)); %> 

We instead kind of render a partial action by calling an action on a controller that handles the logic that we would otherwise have to put in the view.  Now, our main post view action doesn’t have to be concerned about wiring up our sidebar partial view.  We can just call RenderAction() in the view.

ASP.NET MVC AJAX Example using RenderAction

Taking an example from the book Profressional ASP.NET MVC 1.0, we’ll use the submitting a Form Using Ajax example to replace the limited RenderPartial() function with a richer RenderAction() from a Controller’s action to process the results more finely.

In the ProductController, you would add a new method with the signature RenderProductResults(IList<Product> products):

public ActionResult RenderProductResults(IList<Product> products)
{
    // insert some custom logic here, maybe even switch partial views, etc
    //
    if (products.Count > 0)
        return PartialView("ProductSearchResults", products);
    else
        return EmptyResult();
}    

Now, you can update their ajax example to render this new action instead of the RenderPartial() from their example:

<h1>Product Search - jQuery</h1>
<form action="<%= Url.Action("ProductSearch") %>" method="post" id="jform">

  <%= Html.TextBox("query", null, new { size=40 }) %>
  <input type="submit" id="jsubmit" value="go" /> 

</form>

<div id="results2">
  <% Html.RenderAction<ProductController>(c => c.RenderProductResults(Model.Results)); %>
</div>

<script src="http://eduncan911.com/Scripts/jquery-1.3.2.js" type="text/javascript"> </script>
<script src="http://eduncan911.com/Scripts/jquery-form.js" type="text/javascript"> </script>
<script>
$(function() {
$('#jform').submit(function(){
$('#jform').ajaxSubmit({ target: '#results2' });
return false;
});
});
</script>

Look for the Html.RenderAction line above.  What this does is instead of rendering a partial view in the id=”results2” location, you can now render a “partial Action” with the Html.RenderAction() call.  Notice that how we are also passing strongly typed parameters directly into the method?  Yep, fully supported.

By using the RenderAction instead of RenderPartial, you have much more control over what happens with that rendering of the partial (now, rendering of the action).  This gives you an excellent opportunity to remove that complex view logic you may have, and place it in an Action where it belongs!

Custom ViewEngines and Extensions

Now, you can also resolve this logic issue by creating custom ViewEngines, or extensions that expand upon the ViewEngine, HtmlHelper, or UrlHelper.  But, I see those methods as more application-wide common logic (like pagers and server controls for display name).  I do not see that as a solution for your one-off partial view for the ajax response to something.  Think of how bloated your ViewEngine would get, or how many different ViewEngines you’d have to choose from.  No, ViewEngine extensions have a place which I will post about as well.

Summary

When your views start getting complex and messy, it may be time to switch to a RenderAction to handle that logic.  I know I have cleaned up quite a lot using it.  Also, custom view engines have a place in their own right to abstract more global/common logic across the entire site.

 
Reader's Comments
 
Eric Duncan said:
24 September 09 12:13 PM

I am starting a new series that will be focusing on ASP.NET MVC design patterns.&#160; What I will be

 
 
John Dyer said:
25 September 09 11:13 AM

These articles are great. Good work, Eric!

 
Willie said:
25 September 09 11:36 AM

I don't get it...what are you exactly doing that is "better".  Actually, what are you doing?  Is it showing something through ajax at different times or something?  Maybe it's just too early in the morning...

 
25 September 09 11:57 AM

@Willie:

I use an Ajax example to show what you can do with it.  The real power lies in the Html.RenderAction() where you can actually render an action, as a partial view.

Instead of having complex logic in your partial view, you can port that logic to a "Partial Action" on a controller, which will render that partial view for you.  THis gives you the power of putting the complex partial view logic in the controller's action, where it belongs.

I tweaked the post a little to show that one line better.  

 
maoyuan121 said:
23 October 09 5:27 AM

Good work thx

 
25 October 09 11:08 AM

Hi,

I tried what you suggested in this article, works fine without Ajax :) when using ajax i get strange result. In the results2 i have the ProductsSearch view rendered together with ProductSearchResults view instead of ProductSearchResults view only. Can you publish some working code or help me with that.

10x

 
26 October 09 2:32 PM

@Dimitar: My code are only snippets of a larger example in this book, Professional ASP.NET MVC 1.0:  

http://www.amazon.com/gp/product/0470384611?ie=UTF8&tag=eduncan911com-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=0470384611

My code above is not complete code example - it is in that book (a bit larger).  

For example, in the RenderProductResults() action method in the book, you must be checking for an Ajax post and render the partial view instead of the full view.

if (Request.IsMvcAjaxRequest())

Let me know.

Leave a Comment
Comment Policy: HTML is not allowed. Links and line breaks are converted automatically.
(required) 
(optional)
(required) 

 


  
Enter the anti-spam code you see above (required)

 

Comment Notifications
Subscribe to this post's comments using RSS

If you would like to receive an email when updates are made to this post, please register here