In this blog post I want to show examples when to use Html.RenderPartial and Html.RenderAction when creating web sites in ASP.NET MVC using the Razor view engine.
Some background information on Html.RenderAction and Html.RenderPartial
Html.RenderAction is a somewhat contentious feature because it invokes a controller action.
This means each time it’s called a new request (which has to go through the ASP.NET MVC pipeline) is made.
As shown below ASP.NET MVC requests are routed to a controller. It’s the controller that decides what view to call and what data (model) to pass to it.
A view has one responsibility and that’s to render HTML. It’s supposed to be simple, it doesn’t know (or care) where the data in the model comes from.
The thing MVC traditionalists frown upon is the fact that Html.RenderAction can execute child actions in ASP.NET MVC views.
However given that Html.RenderAction just like an AJAX request is a form of sub request, the case against seems somewhat outdated and doesn’t take into the account using Html.RenderAction will allow you to separate concerns of models and controllers as we will see later in this post.
For Html.RenderPartial the content of the partial view is added to content of the host view, either directly to the response output (or by using a StringWriter if you use or Html.Partial).
Examples of when to use Html.RenderPartial and Html.RenderAction
The mockup below shows a blog post with comments and a list of categories in a side bar.
When to use Html.RenderPartial (or Html.Partial)
For the page we’re designing I would create a view to show the blog post and call Html.RenderPartial to output the comments
This means the ShowPost action in the Blog controller
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using WhenToUseRenderActionAndRenderPartial.Models;
namespace WhenToUseRenderActionAndRenderPartial.Controllers
{
public class BlogController : Controller
{
public ActionResult ShowPost()
{
ShowPostViewModel viewModel = new ShowPostViewModel();
viewModel.Post = new Post
{
Title = "Blog Title",
Content = "<p>Lorem ipsum dolor sit amet</p>",
DatePublished = new DateTime(2010, 10, 5)
};
viewModel.Comments = new List<Comment>()
{
new Comment()
{
Author = "Andrew",
Content = "I think",
DateCreated = new DateTime(2010, 10, 5)
},
new Comment()
{
Author = "Bill",
Content = "I reckon",
DateCreated = new DateTime(2010, 10, 5)
},
new Comment()
{
Author = "Chris",
Content = "My opinion",
DateCreated = new DateTime(2010, 10, 6)
}
};
return View(viewModel);
}
}
}
needs to pass a model with properties for the blog post and its comments to the view.
public class Post
{
public string Title { get; set; }
public string Content { get; set; }
public DateTime DatePublished { get; set; }
}
public class Comment
{
public string Content { get; set; }
public string Author { get; set; }
public string ImageUrl { get; set; }
public DateTime DateCreated { get; set; }
}
public class ShowPostViewModel
{
public Post Post { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
When to use Html.RenderAction (or Html.Action)
So while we could add a property for categories to the ShowPostViewModel used for the ShowPost page and use Html.RenderPartial to display them, we have to add a category property to every model in our solution where categories are shown.
public class ShowPostViewModel
{
public Post Post { get; set; }
public IEnumerable<Comment> Comments { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
public class AnotherViewModel
{
public string StringToOutput { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
And using a base class with a categories property and have ViewModel classes inherit from it would just be bloating models with unrelated data and concerns.
public class ViewModelBase
{
public IEnumerable<Category> Categories { get; set; }
}
public class ShowPostViewModel : ViewModelBase
{
public Post Post { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
So if you want to display the same information on multiple pages and the data doesn’t necessarily depend on or isn’t related to the rest of the page, something like a widget to display categories then I would use Html.RenderAction.
As shown in the markup below Html.RenderAction is called from the layout page for the blog post.
Jag Reehal’s Final Thought on ‘When to use Html.RenderPartial and Html.RenderAction in ASP.NET MVC Razor Views’
In cases where a model for the page is already populated with all the information required I would use Html.RenderPartial.
If the same information is shown on multiple pages it’s likely that the data needs to be in its own model so I would use Html.RenderAction. As shown in this post I would say layout pages are going to the most common place Html.RenderAction will be used.
The code used in this post can be downloaded here.
If you recall the reasons you should not have logic in your views are because they will become difficult to read, maintain and test at the very least.
How to decide what CSS class to use for the stock level in a Razor view
The screenshot below shows the three possible messages displayed to the user depending on the number of units in stock.
An example of a Razor view with conditional logic
We could select the CSS class for the corresponding number of units in stock using multiple if statements like this
@inherits System.Web.Mvc.WebViewPage<KeepLogicOutOfYourRazorViews.Models.Product>
@using KeepLogicOutOfYourRazorViews.Helpers
@{
View.Title = "Index";
LayoutPage = "~/Views/Shared/_Layout.cshtml";
}
<div class="example">
@if(Model.UnitsInStock == 0){
<div class="stockLevel stockLevelNone">
<span>Number Of Units In Stock: @Model.UnitsInStock</span>
</div>
}
@if(Model.UnitsInStock > 0 && Model.UnitsInStock <= 20)
{
<div class="stockLevel stockLevelLow">
<span>Number Of Units In Stock: @Model.UnitsInStock</span>
</div>
}
@if(Model.UnitsInStock > 20){
<div class="stockLevel stockLevelHigh">
<span>Number Of Units In Stock: @Model.UnitsInStock</span>
</div>
}
</div>
but there are better ways to achieve our goal.
So what are the alternatives?
We could:
Move the logic for choosing the CSS class into a helper method
Create a method to build the HTML for displaying the number of units in stock
Use a Product data transfer object with a CSS class name property
Pass all the CSS classes to a method which will return the CSS class to use
As I want to concentrate on showing code for Razor view engine, if you want to see the methods, unit tests and discussion about the advantages and disadvantages of each option please refer to the original post.
What the view would look like if we used a method to get the CSS class in a Razor view
The code below shows how we could achieve the same result by calling a method which returns the CSS class for the number of units in stock.
@inherits System.Web.Mvc.WebViewPage<KeepLogicOutOfYourRazorViews.Models.Product>
@using KeepLogicOutOfYourRazorViews.Helpers
@{
View.Title = "Index";
LayoutPage = "~/Views/Shared/_Layout.cshtml";
}
<div class="example">
<div class="stockLevel @ProductCssHelper.GetCssClassForUnitsInStock(Model.UnitsInStock)">
<span>Number Of Units In Stock: @Model.UnitsInStock</span>
</div>
</div>
What the view would look like if we passed the CSS classes to a method in a Razor view
Alternatively, a more designer friendly way would be to pass the CSS classes to a method which returns the appropriate CSS class to use for the stock level.
@inherits System.Web.Mvc.WebViewPage<KeepLogicOutOfYourRazorViews.Models.Product>
@using KeepLogicOutOfYourRazorViews.Helpers
@{
View.Title = "Index";
LayoutPage = "~/Views/Shared/_Layout.cshtml";
}
<div class="example">
<div class="stockLevel
@ProductCssHelper.SelectCssClassForUnitsInStock(
Model.UnitsInStock,
"stockLevelNone",
"stockLevelLow",
"stockLevelHigh"
)">
<span>Number Of Units In Stock: @Model.UnitsInStock</span>
</div>
</div>
Jag Reehal’s Final Thought on ‘ASP.NET MVC Razor View Best Practices – Keep logic out of your Razor views’
Razor does a fantastic job of cleaning up your views, but to keep them even cleaner the best practice to keep logic out of yours view still applies.