A ‘Partial view’ is only a fancy name for a concept well-known to web developers, and especially ASP.NET Web Forms developers: user controls. Since the old days of ASP.NET, a user control has been identified as a custom, reusable component created using the same techniques that are required for creating an ASPX page. In ASP.NET MVC, a partial view is a custom, reusable component created using the same techniques required for creating a HTML view for a particular engine. If you are using Razor as the default view engine, a partial view is simply a Razor view file that is not bound to any layout. By its very nature, a partial view is a reusable view component made of both code and markup.
Each partial view consists of a file with the same extension as any other view supported by the current engine. For Razor, a partial view is a .cshtml file located under the Views project folder, in a specific controller folder or in the Shared folder. As a developer, you identify partial views by name and can reference them in a Razor view file in either of two ways: using the Html.Partial method or Html. RenderPartial method.
Partial views are apparently a very simple topic to master for ASP.NET MVC developers. Partial views are certainly not an application of rocket science but there are many ways to use them more effectively that are little known and that you find out only when you dirty your hands with the nitty-gritty code.
RenderPartial vs Partial
As mentioned, you can incorporate a partial view in a Razor view by using either of two methods: RenderPartial or Partial. The difference between the two methods may look small and harmless, but it may bite at you if you don’t know how to handle it. The key difference between the two methods is that Partial returns a HTML-encoded string while RenderPartial is a void method that writes directly to the response output stream. The usage of the two methods is slightly different:
1 2 |
@Html.Partial("_yourPartialView")@{ Html. RenderPartial("_yourPartialView "); } |
The choice of which to use depends on your requirements. If you need to further manipulate the string being injected in the response stream, you should use Partial; otherwise go with RenderPartial which is-just because it goes straight to the stream-a little faster than Partial.
In the end, the use-cases for partial views fall in either of two camps. The first is when you create a view by composing together various independent pieces of markup, as below.
1 2 3 4 5 6 7 8 |
<body> @{ Html.RenderPartial("_Header"); } @Html.Partial("_Sidebar") <div class="container body-content"> @RenderBody() </div> @{ Html.RenderPartial("_Footer"); } </body> |
In this case, your decision in choosing between RenderPartial or Partial doesn’t change the final effect. However, because RenderPartial is slightly faster, you may prefer using it.
The second use-case is when you are composing markup iteratively, looping through some passed data. Because the method Partial returns the markup as a string, you can store that content to a variable and use it later in the Razor view. Frankly, I wouldn’t say this is a very common scenario or, at least, I don’t remember having run into it in the past. In summary, you might safe enough always using RenderPartial unless you need to access the actual HTML returned by a partial view.
Passing data to partial views
By default, the view engine passes each partial view as a reference to the same view model object it received from the controller. It’s a simple matter of sharing the view context and including system-defined data dictionaries such as ViewData and ViewBag as well.
Most of the time, this behavior is fine. In particular, it is appropriate when you’re using the partial view as a plain subroutine with the main purpose of keeping the code readable and easy to maintain. For example, if you have a partial view to render the footer or header of a view then you can keep things simple by passing no explicit parameter to the partial view, and you still know that the Model object is accessible from within the components.
In other cases, though, you want to use the partial view to iteratively compose a more complex view layout such as a grid of data. This fact changes the perspective of the partial view. In this case, you intend to use the partial view as a generic HTML factory that consumes some specific data, injects it into a HTML template and produces a given result. In this case, you want to pass an explicit parameter to the partial view and you don’t even want it to be the entire view model. Here’s some code that summarizes a common scenario.
1 2 3 4 5 6 7 |
<table class="table table-condensed"> @foreach (var customer in Model.Customers) { @Html.Partial("_CustomerBox", customer) } </table> |
Let’s say you are building a table where each row displays some information about a customer: name, city, address and so forth. The parent view receives the entire list of customers from the controller and iterates through it to populate the grid. In doing so, the parent view calls the partial view repeatedly within a loop and each time it passes a specific customer instance. In this case, you want to give the partial view its own view model reference.
To give a partial view its own view model type, you just replicate in the Razor file the @model directive as you would do in a full Razor view.
1 2 3 4 5 6 7 8 |
@model YourApp.Model.Customer <table class="table table-condensed"> @foreach (var customer in Model.Customers) { @Html.Partial("_CustomerBox", customer) } </table> |
Note that you’ll still be able to access the global ViewData and ViewBag collections from within a partial view. Any value stored in such dictionaries by the controller can still be read from the partial view even when the Model property is overridden in the parent view.
Furthermore, you can still use ViewData and ViewBag to pass additional information to the partial view, outside the view model type. Suppose you add the following to the parent Razor view:
1 |
@{ ViewBag.ExtraField = "Some content"; } |
You can retrieve the same value from within the partial view with the usual syntax.
1 |
<h2>@ViewBag.ExtraField</h2> |
In general you would store all data for the partial view in the specific view model type. However, for quick changes and extensions editing the view model requires a compile step and deploying new binaries to production. This is a trick, instead, that only requires updating Razor files. Note, though, it would work without a compile step only if the data to pass to the partial view can be computed in the parent view or is already available in the view model passed to the parent view.
Render Action Methods
Any current and past Web Forms developer has a deep understanding of the ViewState and is often strongly opinionated about it. In ASP.NET MVC, there’s no such a thing and this makes pages significantly smaller but still leaves developers doing a bit more work-just the work the ViewState was doing on our behalf.
Every time you render a view, the controller is not only responsible for obtaining the data that is specific to the run action and the subsequent view but also for a bunch of static data required by the layout (menu, header, footer, sidebars) and surrounding forms and elements. Unless working over an Ajax request, the controller is always responsible for serving a full view. To avoid the logic of each controller action method getting spoiled by layout concerns and data retrieval, the ASP.NET MVC team introduced render action methods.
A render action is a regular controller method that you invoke from the view by using one of the following HTML helpers: Action or RenderAction.
1 2 |
@Html.Action("action") @{ Html.RenderAction("action"); } |
The difference between Action and RenderAction recalls the subtle difference that we’ve discussed already between Partial and RenderPartial. The Action method returns the markup as a string, whereas RenderAction writes directly to the output stream. You might want to always use RenderAction except when you need to work on the markup as a string before writing it down to the output stream. Here’s a sample render action method.
1 2 3 4 5 6 7 |
public ActionResult Menu() { var options = new MenuOptionsViewModel(); options.Items.Add(new MenuOption {Url="...", Image="..."}); options.Items.Add(new MenuOption {Url="...", Image="..."}); return PartialView(options); } |
With this method in place, no other controller method will be responsible any longer for collection menu data to arrange its next view. The work of collecting menu data will become part of the view’s work. Here’s how each specific view would call back the controller to get menu data and render it.
1 2 3 4 5 |
<div> ... @Html.RenderAction("Menu") ... </div> |
The view receives just the data it needs for its specific purpose and calls back the controller for any surrounding piece of markup it needs to incorporate. The controller just exposes as separate methods pieces of logic to create menus and sidebars. The actual menu logic and markup is isolated into a separate partial view.
Render actions are a bit controversial because some MVC purists argue that the view must be completely passive and just arrange for rendering whatever it receives from the controller. Giving to the view the power of calling back the controller would add logic and intelligence to the view, thus violating the dogma of its total passivity.
Frankly, to me the entire question seems like splitting hairs so I dare say it’s a pure matter of preference how passive you like your views to be. But there’s another aspect of render action methods that I want to call out-and this is way more interesting.
Suppose your view is made of several widgets like menu, header, footer, sidebars and advertising boxes and you use a partial view for each of them. If you like passive views, then you pass the view a model that comprises all the data for all widgets. The view therefore is only responsible for calling partial views and for sharing the view context with them. At the controller level, you typically arrange a single transaction to retrieve all the data you need and pass it in a single shot.
Let’s see what could happen if you use a distinct render action for each widget. Each render action will have its own step for retrieving data. If data has to be read from some database then, in the worst case, you end up having a database transaction for each widget instead of a single database transaction to retrieve all data at the same time.
It’s yet another instance of the longstanding software dilemma of being chatty or being chunky. It should be noted that you don’t automatically run into the chatty problem because you use render actions, but that’s definitely a risk. I’ve seen that happen and I know it can easily happen when developers fall victims of good design for the sake of good design.
Changing the Location of Partial Views
When you use a lot of partial views the Views project folder inevitably becomes crowded. You can place the partial view in the Shared folder or you can store it under the controller-specific subfolder. It would to shared if used by multiple controllers and it would better go to the controller view folder if only used by a single controller. In both cases, when you use a lot of partial views (which is indeed a good thing), it gets harder to locate the partial view file to edit or review.
An easy solution would be to create custom subfolders to organize the content better. Unfortunately, though, changing the structure of the Views folder in an ASP.NET MVC project is not a task you can take arbitrarily.
Personally, I like to have all my partial views grouped under a specific Views subfolder with each controller having its own subfolder. The structure I mean is rendered in the figure.
In the figure you see distinct folders for view layouts (Layout) and partial views (Partial). I also reorganized the regular controller views by adding a Controller folder under Views. For these custom folders to be recognized you must change a few things in the configuration of the view engine of choice, in this case the Razor view engine. Here’s how to customize the engine. The code below goes in global.asax.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
ViewEngines.Engines.Clear(); var customEngine = new RazorViewEngine(); customEngine.PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/Shared/{0}.cshtml", "~/Views/Partial/{0}.cshtml", "~/Views/Partial/{1}/{0}.cshtml" }; this.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/Shared/{0}.cshtml", "~/Views/Controller/{1}/{0}.cshtml" }; this.MasterLocationFormats = new string[] { "~/Views/Shared/{0}.cshtml", "~/Views/Layout/{0}.cshtml" }; ViewEngines.Engines.Add(customEngine); |
As an example, the PartialViewLocationFormats property on the view engine class contains an array of wildcard paths where the engine expects to find the partial views. The paths are expanded using String.Format and can contain up to two placeholders. The former {0} refers to view name; the latter {1} refers to the controller name.
Finally, there’s no widely accepted convention for the name of partial views. Some like to prefix the name with an underscore, and that’s probably the most common approach. Others prefer to use slightly more evocative prefixes like pv _ or whatever else you like.
Summary
Partial views are a common aspect of ASP.NET MVC applications. So common is it that probably everybody uses them without any further thought. Using partial views is easy and effective and I do recommend them. However, the more you use them the more you learn about little and subtle aspects of them. This article just shared a few findings that emerged in years of using partial views.
Load comments